Compare commits
115 Commits
v1.7.0-pre
...
michael-j-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68bd3d8408 | ||
|
|
25ce6979ad | ||
|
|
9145688d35 | ||
|
|
d07300ecb5 | ||
|
|
86c207e389 | ||
|
|
a0a8495597 | ||
|
|
aa90b4ebc3 | ||
|
|
9e93ed1041 | ||
|
|
f8b9ca3b66 | ||
|
|
6863f65640 | ||
|
|
7c327eeb3e | ||
|
|
202c2c0b62 | ||
|
|
da98b1cd74 | ||
|
|
fc0d164a96 | ||
|
|
8153c42fe1 | ||
|
|
97645af336 | ||
|
|
42370a9699 | ||
|
|
1727a62150 | ||
|
|
c170d98a68 | ||
|
|
ae41fae54f | ||
|
|
ffd4dcbb33 | ||
|
|
c724d927bb | ||
|
|
a859d873e6 | ||
|
|
7a9827923f | ||
|
|
142f3ed561 | ||
|
|
2303ab5ffc | ||
|
|
173e49286b | ||
|
|
ca2dbfb4cd | ||
|
|
74171b50af | ||
|
|
b1fcfec14f | ||
|
|
e8ca8ca5ca | ||
|
|
6f5b52058c | ||
|
|
8b9d7c66ab | ||
|
|
f19214ba6b | ||
|
|
2d63a1c416 | ||
|
|
f99eec2eec | ||
|
|
b3ca94b217 | ||
|
|
53c79287a9 | ||
|
|
48e4f17090 | ||
|
|
478044a5a1 | ||
|
|
f25b3d0eaa | ||
|
|
574d3bc39e | ||
|
|
bca4dd178e | ||
|
|
4a76436223 | ||
|
|
32f6a86dc9 | ||
|
|
4075976ad8 | ||
|
|
146597dd4b | ||
|
|
f4ba84a54f | ||
|
|
64fb76484b | ||
|
|
bfade006bd | ||
|
|
c8140d7178 | ||
|
|
070589f718 | ||
|
|
bb86cb52f6 | ||
|
|
7dfb0b54eb | ||
|
|
f0783fcae8 | ||
|
|
68be24d514 | ||
|
|
a5da1a9033 | ||
|
|
fc09681cdd | ||
|
|
6185912151 | ||
|
|
deef919d5b | ||
|
|
fd7b354571 | ||
|
|
be1d53b3b1 | ||
|
|
e0cff36819 | ||
|
|
cea654692e | ||
|
|
c82ffb6c97 | ||
|
|
7400a8c040 | ||
|
|
eda171efa1 | ||
|
|
f881459c0b | ||
|
|
f85109abd2 | ||
|
|
080a823cda | ||
|
|
7e8679151b | ||
|
|
111c501911 | ||
|
|
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 | ||
|
|
ea0a5a6a71 | ||
|
|
d014a176ce | ||
|
|
b9d9b0ea16 | ||
|
|
7e3e4991dc | ||
|
|
57248cd467 |
10
.devcontainer/Dockerfile
Normal file
10
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm
|
||||||
|
|
||||||
|
# update apt-get
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
# download and unzip EmulatorJS from CDN
|
||||||
|
RUN apt-get install -y p7zip-full default-jdk nodejs wget
|
||||||
|
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||||
|
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
|
||||||
|
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z
|
||||||
47
.devcontainer/devcontainer.json
Normal file
47
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 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": "Gaseous C# (.NET)",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
//"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm",
|
||||||
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
|
"service": "development",
|
||||||
|
"workspaceFolder": "/workspace",
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
"forwardPorts": [
|
||||||
|
5198
|
||||||
|
],
|
||||||
|
"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",
|
||||||
|
"PWABuilder.pwa-studio",
|
||||||
|
"ms-azuretools.vscode-docker",
|
||||||
|
"SonarSource.sonarlint-vscode",
|
||||||
|
"oderwat.indent-rainbow"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
25
.devcontainer/docker-compose.yml
Normal file
25
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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=${IGDB_CLIENT_ID}
|
||||||
|
- igdbclientsecret=${IGDB_CLIENT_SECRET}
|
||||||
|
mariadb:
|
||||||
|
hostname: mariadb
|
||||||
|
image: mariadb:latest
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
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
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
- package-ecosystem: "gitsubmodule"
|
- package-ecosystem: "devcontainers"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
allow:
|
|
||||||
- dependency-name: "gaseous-server/wwwroot/emulators/EmulatorJS"
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
4
.github/release.yml
vendored
4
.github/release.yml
vendored
@@ -5,8 +5,12 @@ changelog:
|
|||||||
- '*'
|
- '*'
|
||||||
exclude:
|
exclude:
|
||||||
labels:
|
labels:
|
||||||
|
- note
|
||||||
- bug
|
- bug
|
||||||
- dependencies
|
- dependencies
|
||||||
|
- title: Notes
|
||||||
|
labels:
|
||||||
|
- note
|
||||||
- title: Bug Fixes
|
- title: Bug Fixes
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
|
|||||||
@@ -9,9 +9,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
- name: Install dotnet tool
|
- name: Install dotnet tool
|
||||||
@@ -21,18 +26,37 @@ jobs:
|
|||||||
- name: Sign in to Nuget
|
- 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"
|
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
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Login to GitHub Package Registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push standard image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64 #,linux/arm64
|
file: ./build/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: gaseousgames/gaseousserver:${{ github.ref_name}}
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:${{ github.ref_name}}
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}
|
||||||
|
- name: Build and push image with embedded mariadb
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./build/Dockerfile-EmbeddedDB
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:${{ github.ref_name}}-embeddeddb
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}-embeddeddb
|
||||||
42
.github/workflows/BuildDockerOnTag-Release.yml
vendored
42
.github/workflows/BuildDockerOnTag-Release.yml
vendored
@@ -8,9 +8,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
- name: Install dotnet tool
|
- name: Install dotnet tool
|
||||||
@@ -20,18 +25,39 @@ jobs:
|
|||||||
- name: Sign in to Nuget
|
- 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"
|
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
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Login to GitHub Package Registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push standard image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64 #,linux/arm64
|
file: ./build/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: gaseousgames/gaseousserver:latest,gaseousgames/gaseousserver:${{ github.ref_name}}
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:latest
|
||||||
|
gaseousgames/gaseousserver:${{ github.ref_name}}
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:latest
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}
|
||||||
|
- name: Build and push image with embedded mariadb
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./build/Dockerfile-EmbeddedDB
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:${{ github.ref_name}}-embeddeddb
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}-embeddeddb
|
||||||
61
.github/workflows/BuildNightly.yml
vendored
Normal file
61
.github/workflows/BuildNightly.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Build Nightly Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '15 4 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
- name: Install dotnet tool
|
||||||
|
run: dotnet tool install -g dotnetCampus.TagToVersion
|
||||||
|
- name: Set tag to version
|
||||||
|
run: dotnet TagToVersion -t 0.0.0-nightly
|
||||||
|
- name: Sign in to Nuget
|
||||||
|
run: dotnet nuget add source --username michael-j-green --password ${{ secrets.NUGETKEY }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/gaseous-project/index.json"
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Package Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push standard image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./build/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:nightly
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:nightly
|
||||||
|
- name: Build and push image with embedded mariadb
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./build/Dockerfile-EmbeddedDB
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gaseousgames/gaseousserver:nightly-embeddeddb
|
||||||
|
ghcr.io/gaseous-project/gaseousserver:nightly-embeddeddb
|
||||||
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
|
||||||
85
.github/workflows/codeql.yml
vendored
85
.github/workflows/codeql.yml
vendored
@@ -1,85 +0,0 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main", "branch-v*.*.*" ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ "main" ]
|
|
||||||
schedule:
|
|
||||||
- cron: '21 11 * * 2'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
|
||||||
# - https://gh.io/using-larger-runners
|
|
||||||
# Consider using larger runners for possible analysis time improvements.
|
|
||||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
||||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'csharp', 'javascript' ]
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
|
|
||||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
|
||||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
|
||||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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"
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
|
||||||
|
|
||||||
# - run: |
|
|
||||||
# echo "Run, Build Application using script"
|
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Sign in to Nuget
|
- 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"
|
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
|
- name: Restore dependencies
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -403,3 +403,8 @@ ASALocalRun/
|
|||||||
|
|
||||||
# Local History for Visual Studio
|
# Local History for Visual Studio
|
||||||
.localhistory/
|
.localhistory/
|
||||||
|
gaseous-server/.DS_Store
|
||||||
|
gaseous-server/wwwroot/.DS_Store
|
||||||
|
gaseous-server/wwwroot/emulators/EmulatorJS
|
||||||
|
.devcontainer/.env
|
||||||
|
.mono/
|
||||||
|
|||||||
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
|
|
||||||
|
|||||||
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -10,21 +10,22 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
// 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": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}/gaseous-server",
|
"cwd": "${workspaceFolder}/gaseous-server",
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||||
"serverReadyAction": {
|
"serverReadyAction": {
|
||||||
"action": "openExternally",
|
"action": "openExternally",
|
||||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
"pattern": "\\bNow listening on:\\s+(http?://\\S+)"
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"sourceFileMap": {
|
"sourceFileMap": {
|
||||||
"/Views": "${workspaceFolder}/Views"
|
"/Views": "${workspaceFolder}/Views"
|
||||||
}
|
},
|
||||||
|
"enableStepFiltering": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": ".NET Core Attach",
|
"name": ".NET Core Attach",
|
||||||
|
|||||||
16
Dockerfile
16
Dockerfile
@@ -1,16 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
|
|
||||||
WORKDIR /App
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
# Copy everything
|
|
||||||
COPY . ./
|
|
||||||
# Restore as distinct layers
|
|
||||||
RUN dotnet restore "gaseous-server/gaseous-server.csproj"
|
|
||||||
# Build and publish a release
|
|
||||||
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained false -c Release -o out
|
|
||||||
|
|
||||||
# Build runtime image
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:7.0
|
|
||||||
WORKDIR /App
|
|
||||||
COPY --from=build-env /App/out .
|
|
||||||
ENTRYPOINT ["dotnet", "gaseous-server.dll"]
|
|
||||||
26
Gaseous.sln
26
Gaseous.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 16
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 25.0.1704.4
|
VisualStudioVersion = 25.0.1704.4
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
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
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17FA6F12-8532-420C-9489-CB8FDE42137C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17FA6F12-8532-420C-9489-CB8FDE42137C}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
@@ -21,36 +21,30 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "screenshots", "screenshots"
|
|||||||
screenshots\Game.png = screenshots\Game.png
|
screenshots\Game.png = screenshots\Game.png
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-cli", "gaseous-cli\gaseous-cli.csproj", "{419CC4E4-8932-4E4A-B027-5521AA0CBA85}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.Build.0 = Release|Any CPU
|
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C}
|
{F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
147
LICENSE
147
LICENSE
@@ -1,5 +1,5 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
@@ -7,17 +7,15 @@
|
|||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
software and other kinds of works.
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
The licenses for most software and other practical works are designed
|
||||||
to take away your freedom to share and change the works. By contrast,
|
to take away your freedom to share and change the works. By contrast,
|
||||||
the GNU General Public License is intended to guarantee your freedom to
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
share and change all versions of a program--to make sure it remains free
|
share and change all versions of a program--to make sure it remains free
|
||||||
software for all its users. We, the Free Software Foundation, use the
|
software for all its users.
|
||||||
GNU General Public License for most of our software; it applies also to
|
|
||||||
any other work released this way by its authors. You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
When we speak of free software, we are referring to freedom, not
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
|||||||
want it, that you can change the software or use pieces of it in new
|
want it, that you can change the software or use pieces of it in new
|
||||||
free programs, and that you know you can do these things.
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
To protect your rights, we need to prevent others from denying you
|
Developers that use our General Public Licenses protect your rights
|
||||||
these rights or asking you to surrender the rights. Therefore, you have
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
certain responsibilities if you distribute copies of the software, or if
|
you this License which gives you legal permission to copy, distribute
|
||||||
you modify it: responsibilities to respect the freedom of others.
|
and/or modify the software.
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
A secondary benefit of defending all users' freedom is that
|
||||||
gratis or for a fee, you must pass on to the recipients the same
|
improvements made in alternate versions of the program, if they
|
||||||
freedoms that you received. You must make sure that they, too, receive
|
receive widespread use, become available for other developers to
|
||||||
or can get the source code. And you must show them these terms so they
|
incorporate. Many developers of free software are heartened and
|
||||||
know their rights.
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
Developers that use the GNU GPL protect your rights with two steps:
|
The GNU Affero General Public License is designed specifically to
|
||||||
(1) assert copyright on the software, and (2) offer you this License
|
ensure that, in such cases, the modified source code becomes available
|
||||||
giving you legal permission to copy, distribute and/or modify it.
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
For the developers' and authors' protection, the GPL clearly explains
|
An older license, called the Affero General Public License and
|
||||||
that there is no warranty for this free software. For both users' and
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
authors' sake, the GPL requires that modified versions be marked as
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
changed, so that their problems will not be attributed erroneously to
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
authors of previous versions.
|
this license.
|
||||||
|
|
||||||
Some devices are designed to deny users access to install or run
|
|
||||||
modified versions of the software inside them, although the manufacturer
|
|
||||||
can do so. This is fundamentally incompatible with the aim of
|
|
||||||
protecting users' freedom to change the software. The systematic
|
|
||||||
pattern of such abuse occurs in the area of products for individuals to
|
|
||||||
use, which is precisely where it is most unacceptable. Therefore, we
|
|
||||||
have designed this version of the GPL to prohibit the practice for those
|
|
||||||
products. If such problems arise substantially in other domains, we
|
|
||||||
stand ready to extend this provision to those domains in future versions
|
|
||||||
of the GPL, as needed to protect the freedom of users.
|
|
||||||
|
|
||||||
Finally, every program is threatened constantly by software patents.
|
|
||||||
States should not allow patents to restrict development and use of
|
|
||||||
software on general-purpose computers, but in those that do, we wish to
|
|
||||||
avoid the special danger that patents applied to a free program could
|
|
||||||
make it effectively proprietary. To prevent this, the GPL assures that
|
|
||||||
patents cannot be used to render the program non-free.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
@@ -72,7 +60,7 @@ modification follow.
|
|||||||
|
|
||||||
0. Definitions.
|
0. Definitions.
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
works, such as semiconductor masks.
|
works, such as semiconductor masks.
|
||||||
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
|||||||
the Program, the only way you could satisfy both those terms and this
|
the Program, the only way you could satisfy both those terms and this
|
||||||
License would be to refrain entirely from conveying the Program.
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
Notwithstanding any other provision of this License, you have
|
||||||
permission to link or combine any covered work with a work licensed
|
permission to link or combine any covered work with a work licensed
|
||||||
under version 3 of the GNU Affero General Public License into a single
|
under version 3 of the GNU General Public License into a single
|
||||||
combined work, and to convey the resulting work. The terms of this
|
combined work, and to convey the resulting work. The terms of this
|
||||||
License will continue to apply to the part which is the covered work,
|
License will continue to apply to the part which is the covered work,
|
||||||
but the special requirements of the GNU Affero General Public License,
|
but the work with which it is combined will remain governed by version
|
||||||
section 13, concerning interaction through a network will apply to the
|
3 of the GNU General Public License.
|
||||||
combination as such.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
the GNU General Public License from time to time. Such new versions will
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
address new problems or concerns.
|
address new problems or concerns.
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
Each version is given a distinguishing version number. If the
|
||||||
Program specifies that a certain numbered version of the GNU General
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
Public License "or any later version" applies to it, you have the
|
Public License "or any later version" applies to it, you have the
|
||||||
option of following the terms and conditions either of that numbered
|
option of following the terms and conditions either of that numbered
|
||||||
version or of any later version published by the Free Software
|
version or of any later version published by the Free Software
|
||||||
Foundation. If the Program does not specify a version number of the
|
Foundation. If the Program does not specify a version number of the
|
||||||
GNU General Public License, you may choose any version ever published
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
by the Free Software Foundation.
|
by the Free Software Foundation.
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
If the Program specifies that a proxy can decide which future
|
||||||
versions of the GNU General Public License can be used, that proxy's
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
public statement of acceptance of a version permanently authorizes you
|
public statement of acceptance of a version permanently authorizes you
|
||||||
to choose that version for the Program.
|
to choose that version for the Program.
|
||||||
|
|
||||||
@@ -631,44 +629,33 @@ to attach them to the start of each source file to most effectively
|
|||||||
state the exclusion of warranty; and each file should have at least
|
state the exclusion of warranty; and each file should have at least
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
Gaseous
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
Copyright (C) 2023 Gaseous
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU Affero General Public License as published
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
If your software can interact with users remotely through a computer
|
||||||
notice like this when it starts in an interactive mode:
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
<program> Copyright (C) 2023 Gaseous
|
interface could display a "Source" link that leads users to an archive
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
of the code. There are many ways you could offer source, and different
|
||||||
This is free software, and you are welcome to redistribute it
|
solutions will be better for different programs; see section 13 for the
|
||||||
under certain conditions; type `show c' for details.
|
specific requirements.
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
<https://www.gnu.org/licenses/>.
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
||||||
|
|||||||
204
README.MD
204
README.MD
@@ -1,194 +1,42 @@
|
|||||||
# Gaseous Server
|
[](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildNightly.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml)
|
||||||
|
# <img src="./logo.png" height="28" style="float: right;" /> Gaseous Server
|
||||||
|
|
||||||
This is the server for the Gaseous system. It offers ROM and title management, as well as some basic in browser emulation of those ROM's.
|
This is the server for the Gaseous system. It offers ROM and title management, as well as some basic in browser emulation of those ROMs.
|
||||||
|
|
||||||
## Warning
|
Version 1.7.0 and later contain user authentication, and can be exposed to the internet. However, it is recommended to not expose the server to the internet if you're not actively using it remotely, or if you have alternative means to access it remotely like a VPN.
|
||||||
|
|
||||||
This project is currently not suitable for being exposed to the internet.
|
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**.
|
||||||
1. there is currently 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**.
|
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Requirements
|
## 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.
|
* 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.
|
* 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
|
* Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation
|
||||||
|
|
||||||
## Third Party Projects
|
# Installation
|
||||||
The following projects are used by Gaseous
|
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
|
||||||
* 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
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
# Adding Content
|
# 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.
|
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
|
||||||
|
|
||||||
These signature DAT files contain a list of titles with hashes for many of the ROM images that have been found by the community.
|
## 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
|
||||||
|
|
||||||
Currently supported DAT's:
|
## Third Party Projects
|
||||||
* TOSEC: https://www.tosecdev.org/downloads/category/56-2023-01-23
|
The following projects are used by Gaseous
|
||||||
* MAME Arcade and MAME Mess: https://www.progettosnaps.net/dats/MAME
|
* [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)
|
||||||
|
|
||||||
If there are other DAT's you'd like to see support for, please raise an issue with a link to the DAT's.
|
## Discord Server
|
||||||
|
Join our Discord server: https://discord.gg/Nhu7wpT3k4
|
||||||
## 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.
|
|
||||||
|
|||||||
67
build/Dockerfile
Normal file
67
build/Dockerfile
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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 .. ./
|
||||||
|
|
||||||
|
# Build Gaseous Web Server
|
||||||
|
# Restore as distinct layers
|
||||||
|
RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
|
||||||
|
# Build and publish a release
|
||||||
|
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
|
||||||
|
|
||||||
|
# update apt-get
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
# download and unzip EmulatorJS from CDN
|
||||||
|
RUN apt-get install -y p7zip-full
|
||||||
|
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||||
|
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
|
||||||
|
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z
|
||||||
|
|
||||||
|
# clean up apt-get
|
||||||
|
RUN apt-get clean && rm -rf /var/lib/apt/lists
|
||||||
|
|
||||||
|
# Build runtime image
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
ENV INDOCKER=1
|
||||||
|
WORKDIR /App
|
||||||
|
COPY --from=build-env /App/out .
|
||||||
|
|
||||||
|
# variables
|
||||||
|
ARG PUID=1000
|
||||||
|
ARG PGID=1000
|
||||||
|
ARG dbhost=localhost
|
||||||
|
ARG dbuser=root
|
||||||
|
ARG dbpass=gaseous
|
||||||
|
|
||||||
|
ENV PUID=${PUID}
|
||||||
|
ENV PGID=${PGID}
|
||||||
|
ENV dbhost=${dbhost}
|
||||||
|
ENV dbuser=${dbuser}
|
||||||
|
ENV dbpass=${dbpass}
|
||||||
|
|
||||||
|
# install supervisord
|
||||||
|
RUN apt-get update && apt-get install -y supervisor
|
||||||
|
COPY ../build/standard/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
RUN mkdir -p /var/run/supervisord
|
||||||
|
RUN mkdir -p /var/log/supervisord
|
||||||
|
|
||||||
|
# clean up apt-get
|
||||||
|
RUN apt-get clean && rm -rf /var/lib/apt/lists
|
||||||
|
|
||||||
|
# copy entrypoint
|
||||||
|
COPY ../build/standard/entrypoint.sh /usr/sbin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/sbin/entrypoint.sh
|
||||||
|
|
||||||
|
# volumes
|
||||||
|
VOLUME /home/gaseous/.gaseous-server
|
||||||
|
|
||||||
|
# start services
|
||||||
|
ENTRYPOINT [ "/usr/sbin/entrypoint.sh" ]
|
||||||
73
build/Dockerfile-EmbeddedDB
Normal file
73
build/Dockerfile-EmbeddedDB
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
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 .. ./
|
||||||
|
|
||||||
|
# Build Gaseous Web Server
|
||||||
|
# Restore as distinct layers
|
||||||
|
RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
|
||||||
|
# Build and publish a release
|
||||||
|
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
|
||||||
|
|
||||||
|
# update apt-get
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
# download and unzip EmulatorJS from CDN
|
||||||
|
RUN apt-get install -y p7zip-full
|
||||||
|
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||||
|
RUN wget https://cdn.emulatorjs.org/releases/4.1.1.7z
|
||||||
|
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.1.1.7z
|
||||||
|
|
||||||
|
# Build runtime image
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
ENV INDOCKER=1
|
||||||
|
WORKDIR /App
|
||||||
|
COPY --from=build-env /App/out .
|
||||||
|
|
||||||
|
# variables
|
||||||
|
ARG PUID=1000
|
||||||
|
ARG PGID=1000
|
||||||
|
ARG dbhost=localhost
|
||||||
|
ARG dbuser=root
|
||||||
|
ARG dbpass=gaseous
|
||||||
|
ARG MARIADB_ROOT_PASSWORD=$dbpass
|
||||||
|
|
||||||
|
ENV PUID=${PUID}
|
||||||
|
ENV PGID=${PGID}
|
||||||
|
ENV dbhost=${dbhost}
|
||||||
|
ENV dbuser=${dbuser}
|
||||||
|
ENV dbpass=${dbpass}
|
||||||
|
ENV MARIADB_ROOT_PASSWORD=${dbpass}
|
||||||
|
|
||||||
|
# install mariadb
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive && \
|
||||||
|
apt-get update && apt-get install -y mariadb-server
|
||||||
|
RUN mkdir -p /run/mysqld
|
||||||
|
COPY ../build/embeddeddb/mariadb.sh /usr/sbin/start-mariadb.sh
|
||||||
|
RUN chmod +x /usr/sbin/start-mariadb.sh
|
||||||
|
|
||||||
|
# install supervisord
|
||||||
|
RUN apt-get install -y supervisor
|
||||||
|
COPY ../build/embeddeddb/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
RUN mkdir -p /var/run/supervisord
|
||||||
|
RUN mkdir -p /var/log/supervisord
|
||||||
|
|
||||||
|
# clean up apt-get
|
||||||
|
RUN apt-get clean && rm -rf /var/lib/apt/lists
|
||||||
|
|
||||||
|
# copy entrypoint
|
||||||
|
COPY ../build/embeddeddb/entrypoint.sh /usr/sbin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/sbin/entrypoint.sh
|
||||||
|
|
||||||
|
# volumes
|
||||||
|
VOLUME /home/gaseous/.gaseous-server /var/lib/mysql
|
||||||
|
|
||||||
|
# start services
|
||||||
|
ENTRYPOINT [ "/usr/sbin/entrypoint.sh" ]
|
||||||
21
build/embeddeddb/entrypoint.sh
Normal file
21
build/embeddeddb/entrypoint.sh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# create the user
|
||||||
|
echo "Creating user gaseous with UID ${PUID} and GID ${PGID}"
|
||||||
|
groupadd -g ${PGID} gaseous
|
||||||
|
useradd -u ${PUID} -g ${PGID} -m gaseous -d /home/gaseous -G sudo
|
||||||
|
usermod -p "*" gaseous
|
||||||
|
mkdir -p /home/gaseous/.aspnet
|
||||||
|
chown -R ${PUID} /App /home/gaseous/.aspnet
|
||||||
|
chgrp -R ${PGID} /App /home/gaseous/.aspnet
|
||||||
|
mkdir -p /home/gaseous/.gaseous-server
|
||||||
|
chown -R ${PUID} /App /home/gaseous/.gaseous-server
|
||||||
|
chgrp -R ${PGID} /App /home/gaseous/.gaseous-server
|
||||||
|
|
||||||
|
# Set MariaDB permissions
|
||||||
|
mkdir -p /var/lib/mysql /var/log/mariadb /run/mysqld
|
||||||
|
chown -R ${PUID} /var/lib/mysql /var/log/mariadb /run/mysqld
|
||||||
|
chgrp -R ${PGID} /var/lib/mysql /var/log/mariadb /run/mysqld
|
||||||
|
|
||||||
|
# Start supervisord and services
|
||||||
|
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||||
25
build/embeddeddb/mariadb.sh
Normal file
25
build/embeddeddb/mariadb.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# install the database
|
||||||
|
echo "Installing MariaDB"
|
||||||
|
/usr/bin/mariadb-install-db --datadir=/var/lib/mysql --user=gaseous
|
||||||
|
|
||||||
|
# start the database server without network or grant tables
|
||||||
|
echo "Starting MariaDB"
|
||||||
|
/usr/sbin/mariadbd --datadir=/var/lib/mysql --skip-grant-tables --skip-networking &
|
||||||
|
|
||||||
|
# wait for the server to start
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# change the root password
|
||||||
|
echo "Setting MariaDB root password"
|
||||||
|
mariadb -u root -e "FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '$MARIADB_ROOT_PASSWORD'; ALTER USER 'gaseous'@'localhost' IDENTIFIED BY '$MARIADB_ROOT_PASSWORD'; FLUSH PRIVILEGES; SHUTDOWN;"
|
||||||
|
|
||||||
|
# stop the server
|
||||||
|
sleep 5
|
||||||
|
echo "Stopping MariaDB"
|
||||||
|
killall mariadbd
|
||||||
|
|
||||||
|
# start the server normally
|
||||||
|
echo "Starting MariaDB"
|
||||||
|
/usr/sbin/mariadbd --datadir=/var/lib/mysql --user=gaseous
|
||||||
37
build/embeddeddb/supervisord.conf
Normal file
37
build/embeddeddb/supervisord.conf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[supervisord]
|
||||||
|
user=root
|
||||||
|
nodaemon=true
|
||||||
|
logfile=/var/log/supervisord/supervisord.log
|
||||||
|
logfile_maxbytes=50
|
||||||
|
logfile_backups=5
|
||||||
|
pidfile=/var/run/supervisord/supervisord.pid
|
||||||
|
loglevel = info
|
||||||
|
|
||||||
|
[unix_http_server]
|
||||||
|
file=/var/run/supervisord/supervisor.sock
|
||||||
|
chmod=0700
|
||||||
|
|
||||||
|
[rpcinterface:supervisor]
|
||||||
|
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||||
|
|
||||||
|
[supervisorctl]
|
||||||
|
serverurl=unix:///var/run/supervisord/supervisor.sock
|
||||||
|
|
||||||
|
[program:mariadb]
|
||||||
|
user=gaseous
|
||||||
|
command=bash -c "/usr/sbin/start-mariadb.sh"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/dev/fd/1
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:gaseous-server]
|
||||||
|
user=gaseous
|
||||||
|
command=dotnet /App/gaseous-server.dll
|
||||||
|
environment=HOME="/home/gaseous",USER="gaseous"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/dev/fd/1
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
16
build/standard/entrypoint.sh
Normal file
16
build/standard/entrypoint.sh
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# create the user
|
||||||
|
echo "Creating user gaseous with UID ${PUID} and GID ${PGID}"
|
||||||
|
groupadd -g ${PGID} gaseous
|
||||||
|
useradd -u ${PUID} -g ${PGID} -m gaseous -d /home/gaseous -G sudo
|
||||||
|
usermod -p "*" gaseous
|
||||||
|
mkdir -p /home/gaseous/.aspnet
|
||||||
|
chown -R ${PUID} /App /home/gaseous/.aspnet
|
||||||
|
chgrp -R ${PGID} /App /home/gaseous/.aspnet
|
||||||
|
mkdir -p /home/gaseous/.gaseous-server
|
||||||
|
chown -R ${PUID} /App /home/gaseous/.gaseous-server
|
||||||
|
chgrp -R ${PGID} /App /home/gaseous/.gaseous-server
|
||||||
|
|
||||||
|
# Start supervisord and services
|
||||||
|
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||||
28
build/standard/supervisord.conf
Normal file
28
build/standard/supervisord.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[supervisord]
|
||||||
|
user=root
|
||||||
|
nodaemon=true
|
||||||
|
logfile=/var/log/supervisord/supervisord.log
|
||||||
|
logfile_maxbytes=50
|
||||||
|
logfile_backups=5
|
||||||
|
pidfile=/var/run/supervisord/supervisord.pid
|
||||||
|
loglevel = info
|
||||||
|
|
||||||
|
[unix_http_server]
|
||||||
|
file=/var/run/supervisord/supervisor.sock
|
||||||
|
chmod=0700
|
||||||
|
|
||||||
|
[rpcinterface:supervisor]
|
||||||
|
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||||
|
|
||||||
|
[supervisorctl]
|
||||||
|
serverurl=unix:///var/run/supervisord/supervisor.sock
|
||||||
|
|
||||||
|
[program:gaseous-server]
|
||||||
|
user=gaseous
|
||||||
|
command=dotnet /App/gaseous-server.dll
|
||||||
|
environment=HOME="/home/gaseous",USER="gaseous"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/dev/fd/1
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
@@ -4,6 +4,7 @@ services:
|
|||||||
container_name: gaseous-server
|
container_name: gaseous-server
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
|
dockerfile: ./build/Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- gaseous
|
- gaseous
|
||||||
@@ -12,8 +13,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 5198:80
|
- 5198:80
|
||||||
volumes:
|
volumes:
|
||||||
- gs:/root/.gaseous-server
|
- gs:/home/gaseous/.gaseous-server
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=Australia/Sydney
|
||||||
- dbhost=gsdb
|
- dbhost=gsdb
|
||||||
- dbuser=root
|
- dbuser=root
|
||||||
- dbpass=gaseous
|
- dbpass=gaseous
|
||||||
@@ -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: mariadb
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- gaseous
|
|
||||||
volumes:
|
|
||||||
- gsdb:/var/lib/mysql
|
|
||||||
environment:
|
|
||||||
- MARIADB_ROOT_PASSWORD=gaseous
|
|
||||||
- MARIADB_USER=gaseous
|
|
||||||
- MARIADB_PASSWORD=gaseous
|
|
||||||
networks:
|
|
||||||
gaseous:
|
|
||||||
driver: bridge
|
|
||||||
volumes:
|
|
||||||
gs:
|
|
||||||
gsdb:
|
|
||||||
@@ -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:
|
|
||||||
354
gaseous-cli/Program.cs
Normal file
354
gaseous-cli/Program.cs
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
using System;
|
||||||
|
using Authentication;
|
||||||
|
using gaseous_server.Classes;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
/* ------------------------------------------------- */
|
||||||
|
/* This tool is a CLI tool that is used to manage */
|
||||||
|
/* the Gaseous Server. */
|
||||||
|
/* Functions such as user management, and backups */
|
||||||
|
/* are available. */
|
||||||
|
/* ------------------------------------------------- */
|
||||||
|
|
||||||
|
// load app settings
|
||||||
|
Config.InitSettings();
|
||||||
|
|
||||||
|
// set up database connection
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
|
// set up identity
|
||||||
|
IServiceCollection services = new ServiceCollection();
|
||||||
|
services.AddLogging();
|
||||||
|
|
||||||
|
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
|
||||||
|
{
|
||||||
|
options.Password.RequireDigit = true;
|
||||||
|
options.Password.RequireLowercase = true;
|
||||||
|
options.Password.RequireNonAlphanumeric = false;
|
||||||
|
options.Password.RequireUppercase = true;
|
||||||
|
options.Password.RequiredLength = 10;
|
||||||
|
options.User.AllowedUserNameCharacters = null;
|
||||||
|
options.User.RequireUniqueEmail = true;
|
||||||
|
options.SignIn.RequireConfirmedPhoneNumber = false;
|
||||||
|
options.SignIn.RequireConfirmedEmail = false;
|
||||||
|
options.SignIn.RequireConfirmedAccount = false;
|
||||||
|
})
|
||||||
|
.AddUserStore<UserStore>()
|
||||||
|
.AddRoleStore<RoleStore>()
|
||||||
|
;
|
||||||
|
services.AddScoped<UserStore>();
|
||||||
|
services.AddScoped<RoleStore>();
|
||||||
|
|
||||||
|
services.AddTransient<IUserStore<ApplicationUser>, UserStore>();
|
||||||
|
services.AddTransient<IRoleStore<ApplicationRole>, RoleStore>();
|
||||||
|
var userManager = services.BuildServiceProvider().GetService<UserManager<ApplicationUser>>();
|
||||||
|
|
||||||
|
// load the command line arguments
|
||||||
|
string[] cmdArgs = Environment.GetCommandLineArgs();
|
||||||
|
|
||||||
|
// check if the user has entered any arguments
|
||||||
|
if (cmdArgs.Length == 1)
|
||||||
|
{
|
||||||
|
// no arguments were entered
|
||||||
|
Console.WriteLine("Gaseous CLI - A tool for managing the Gaseous Server");
|
||||||
|
Console.WriteLine("Usage: gaseous-cli [command] [options]");
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
Console.WriteLine(" user [command] [options] - Manage users");
|
||||||
|
Console.WriteLine(" role [command] [options] - Manage roles");
|
||||||
|
// Console.WriteLine(" backup [command] [options] - Manage backups");
|
||||||
|
// Console.WriteLine(" restore [command] [options] - Restore backups");
|
||||||
|
Console.WriteLine(" help - Display this help message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the help command
|
||||||
|
if (cmdArgs[1] == "help")
|
||||||
|
{
|
||||||
|
// display the help message
|
||||||
|
Console.WriteLine("Gaseous CLI - A tool for managing the Gaseous Server");
|
||||||
|
Console.WriteLine("Usage: gaseous-cli [command] [options]");
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
Console.WriteLine(" user [command] [options] - Manage users");
|
||||||
|
Console.WriteLine(" role [command] [options] - Manage roles");
|
||||||
|
// Console.WriteLine(" backup [command] [options] - Manage backups");
|
||||||
|
// Console.WriteLine(" restore [command] [options] - Restore backups");
|
||||||
|
Console.WriteLine(" help - Display this help message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the user command
|
||||||
|
if (cmdArgs[1] == "user")
|
||||||
|
{
|
||||||
|
// check if the user has entered any arguments
|
||||||
|
if (cmdArgs.Length == 2)
|
||||||
|
{
|
||||||
|
// no arguments were entered
|
||||||
|
Console.WriteLine("User Management");
|
||||||
|
Console.WriteLine("Usage: gaseous-cli user [command] [options]");
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
Console.WriteLine(" add [username] [password] - Add a new user");
|
||||||
|
Console.WriteLine(" delete [username] - Delete a user");
|
||||||
|
Console.WriteLine(" resetpassword [username] [password] - Reset a user's password");
|
||||||
|
Console.WriteLine(" list - List all users");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the add command
|
||||||
|
if (cmdArgs[2] == "add")
|
||||||
|
{
|
||||||
|
// check if the user has entered the username and password
|
||||||
|
if (cmdArgs.Length < 5)
|
||||||
|
{
|
||||||
|
// the username and password were not entered
|
||||||
|
Console.WriteLine("Error: Please enter a username and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new user
|
||||||
|
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||||
|
if (userTable.GetUserByEmail(cmdArgs[3]) != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: User already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the user object
|
||||||
|
ApplicationUser user = new ApplicationUser
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
Email = cmdArgs[3],
|
||||||
|
NormalizedEmail = cmdArgs[3].ToUpper(),
|
||||||
|
EmailConfirmed = true,
|
||||||
|
UserName = cmdArgs[3],
|
||||||
|
NormalizedUserName = cmdArgs[3].ToUpper()
|
||||||
|
};
|
||||||
|
|
||||||
|
// create the password
|
||||||
|
PasswordHasher<ApplicationUser> passwordHasher = new PasswordHasher<ApplicationUser>();
|
||||||
|
user.PasswordHash = passwordHasher.HashPassword(user, cmdArgs[4]);
|
||||||
|
|
||||||
|
await userManager.CreateAsync(user);
|
||||||
|
await userManager.AddToRoleAsync(user, "Player");
|
||||||
|
|
||||||
|
Console.WriteLine("User created successfully with default role: Player");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the delete command
|
||||||
|
if (cmdArgs[2] == "delete")
|
||||||
|
{
|
||||||
|
// check if the user has entered the username
|
||||||
|
if (cmdArgs.Length < 4)
|
||||||
|
{
|
||||||
|
// the username was not entered
|
||||||
|
Console.WriteLine("Error: Please enter a username");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the user
|
||||||
|
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||||
|
ApplicationUser user = userTable.GetUserByEmail(cmdArgs[3]);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: User not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await userManager.DeleteAsync(user);
|
||||||
|
|
||||||
|
Console.WriteLine("User deleted successfully");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the resetpassword command
|
||||||
|
if (cmdArgs[2] == "resetpassword")
|
||||||
|
{
|
||||||
|
// check if the user has entered the username and password
|
||||||
|
if (cmdArgs.Length < 5)
|
||||||
|
{
|
||||||
|
// the username and password were not entered
|
||||||
|
Console.WriteLine("Error: Please enter a username and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the user's password
|
||||||
|
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||||
|
ApplicationUser user = userTable.GetUserByEmail(cmdArgs[3]);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: User not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the password
|
||||||
|
PasswordHasher<ApplicationUser> passwordHasher = new PasswordHasher<ApplicationUser>();
|
||||||
|
user.PasswordHash = passwordHasher.HashPassword(user, cmdArgs[4]);
|
||||||
|
|
||||||
|
await userManager.UpdateAsync(user);
|
||||||
|
|
||||||
|
Console.WriteLine("Password reset successfully");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the list command
|
||||||
|
if (cmdArgs[2] == "list")
|
||||||
|
{
|
||||||
|
// list all users
|
||||||
|
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||||
|
var userList = userTable.GetUsers();
|
||||||
|
foreach (var user in userList)
|
||||||
|
{
|
||||||
|
var roles = await userManager.GetRolesAsync(user);
|
||||||
|
Console.WriteLine(user.Email + " - " + string.Join(", ", roles));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the role command
|
||||||
|
if (cmdArgs[1] == "role")
|
||||||
|
{
|
||||||
|
// check if the user has entered any arguments
|
||||||
|
if (cmdArgs.Length == 2)
|
||||||
|
{
|
||||||
|
// no arguments were entered
|
||||||
|
Console.WriteLine("Role Management");
|
||||||
|
Console.WriteLine("Usage: gaseous-cli role [command] [options]");
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
Console.WriteLine(" set [username] [role] - Set the role of a user");
|
||||||
|
Console.WriteLine(" list - List all roles");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the role command
|
||||||
|
if (cmdArgs[2] == "set")
|
||||||
|
{
|
||||||
|
// check if the user has entered the username and role
|
||||||
|
if (cmdArgs.Length < 5)
|
||||||
|
{
|
||||||
|
// the username and role were not entered
|
||||||
|
Console.WriteLine("Error: Please enter a username and role");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the role of the user
|
||||||
|
UserTable<ApplicationUser> userTable = new UserTable<ApplicationUser>(db);
|
||||||
|
ApplicationUser user = userTable.GetUserByEmail(cmdArgs[3]);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: User not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove all existing roles from user
|
||||||
|
var roles = await userManager.GetRolesAsync(user);
|
||||||
|
await userManager.RemoveFromRolesAsync(user, roles.ToArray());
|
||||||
|
|
||||||
|
// add the new role to the user
|
||||||
|
await userManager.AddToRoleAsync(user, cmdArgs[4]);
|
||||||
|
|
||||||
|
Console.WriteLine("Role set successfully");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user has entered the list command
|
||||||
|
if (cmdArgs[2] == "list")
|
||||||
|
{
|
||||||
|
// list all roles
|
||||||
|
string[] roles = { "Player", "Gamer", "Admin" };
|
||||||
|
foreach (var role in roles)
|
||||||
|
{
|
||||||
|
Console.WriteLine(role);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // check if the user has entered the backup command
|
||||||
|
// if (cmdArgs[1] == "backup")
|
||||||
|
// {
|
||||||
|
// // check if the user has entered any arguments
|
||||||
|
// if (cmdArgs.Length == 2)
|
||||||
|
// {
|
||||||
|
// // no arguments were entered
|
||||||
|
// Console.WriteLine("Backup Management");
|
||||||
|
// Console.WriteLine("Usage: gaseous-cli backup [command] [options]");
|
||||||
|
// Console.WriteLine("Commands:");
|
||||||
|
// Console.WriteLine(" create - Create a backup");
|
||||||
|
// Console.WriteLine(" list - List all backups");
|
||||||
|
// Console.WriteLine(" remove [backup_id] - Remove a backup");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // check if the user has entered the create command
|
||||||
|
// if (cmdArgs[2] == "create")
|
||||||
|
// {
|
||||||
|
// // create a backup
|
||||||
|
// Backup.CreateBackup();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // check if the user has entered the list command
|
||||||
|
// if (cmdArgs[2] == "list")
|
||||||
|
// {
|
||||||
|
// // list all backups
|
||||||
|
// Backup.ListBackups();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // check if the user has entered the remove command
|
||||||
|
// if (cmdArgs[2] == "remove")
|
||||||
|
// {
|
||||||
|
// // check if the user has entered the backup id
|
||||||
|
// if (cmdArgs.Length < 4)
|
||||||
|
// {
|
||||||
|
// // the backup id was not entered
|
||||||
|
// Console.WriteLine("Error: Please enter a backup id");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // remove the backup
|
||||||
|
// Backup.RemoveBackup(cmdArgs[3]);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // check if the user has entered the restore command
|
||||||
|
// if (cmdArgs[1] == "restore")
|
||||||
|
// {
|
||||||
|
// // check if the user has entered any arguments
|
||||||
|
// if (cmdArgs.Length == 2)
|
||||||
|
// {
|
||||||
|
// // no arguments were entered
|
||||||
|
// Console.WriteLine("Restore Management");
|
||||||
|
// Console.WriteLine("Usage: gaseous-cli restore [command] [options]");
|
||||||
|
// Console.WriteLine("Commands:");
|
||||||
|
// Console.WriteLine(" restore [backup_id] - Restore a backup");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // check if the user has entered the restore command
|
||||||
|
// if (cmdArgs[2] == "restore")
|
||||||
|
// {
|
||||||
|
// // check if the user has entered the backup id
|
||||||
|
// if (cmdArgs.Length < 4)
|
||||||
|
// {
|
||||||
|
// // the backup id was not entered
|
||||||
|
// Console.WriteLine("Error: Please enter a backup id");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // restore the backup
|
||||||
|
// Restore.RestoreBackup(cmdArgs[3]);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// the user entered an invalid command
|
||||||
|
Console.WriteLine("Error: Invalid command");
|
||||||
28
gaseous-cli/gaseous-cli.csproj
Normal file
28
gaseous-cli/gaseous-cli.csproj
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RootNamespace>gaseous_cli</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<DocumentationFile>bin\Debug\net8.0\gaseous-cli.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<DocumentationFile>bin\Release\net8.0\gaseous-cli.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="MySqlConnector" Version="2.3.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\gaseous-server\gaseous-server.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
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 SecurityProfileViewModel SecurityProfile { get; set; }
|
||||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||||
|
public Guid ProfileId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ namespace Authentication
|
|||||||
var roleName = GetRoleName(roleId);
|
var roleName = GetRoleName(roleId);
|
||||||
ApplicationRole? role = null;
|
ApplicationRole? role = null;
|
||||||
|
|
||||||
if(roleName != null)
|
if (roleName != null)
|
||||||
{
|
{
|
||||||
role = new ApplicationRole();
|
role = new ApplicationRole();
|
||||||
role.Id = roleId;
|
role.Id = roleId;
|
||||||
@@ -153,7 +153,7 @@ namespace Authentication
|
|||||||
string commandText = "Select Name from Roles";
|
string commandText = "Select Name from Roles";
|
||||||
|
|
||||||
var rows = _database.ExecuteCMDDict(commandText);
|
var rows = _database.ExecuteCMDDict(commandText);
|
||||||
foreach(Dictionary<string, object> row in rows)
|
foreach (Dictionary<string, object> row in rows)
|
||||||
{
|
{
|
||||||
ApplicationRole role = (ApplicationRole)Activator.CreateInstance(typeof(ApplicationRole));
|
ApplicationRole role = (ApplicationRole)Activator.CreateInstance(typeof(ApplicationRole));
|
||||||
role.Id = (string)row["Id"];
|
role.Id = (string)row["Id"];
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Authentication
|
|||||||
parameters.Add("@userId", userId);
|
parameters.Add("@userId", userId);
|
||||||
|
|
||||||
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
|
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
|
||||||
foreach(DataRow row in rows)
|
foreach (DataRow row in rows)
|
||||||
{
|
{
|
||||||
roles.Add((string)row["Name"]);
|
roles.Add((string)row["Name"]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Authentication
|
|||||||
/// Class that represents the Users table in the MySQL Database
|
/// Class that represents the Users table in the MySQL Database
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserTable<TUser>
|
public class UserTable<TUser>
|
||||||
where TUser :ApplicationUser
|
where TUser : ApplicationUser
|
||||||
{
|
{
|
||||||
private Database _database;
|
private Database _database;
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ namespace Authentication
|
|||||||
public TUser GetUserById(string userId)
|
public TUser GetUserById(string userId)
|
||||||
{
|
{
|
||||||
TUser user = null;
|
TUser user = null;
|
||||||
string commandText = "Select * from Users where Id = @id";
|
string commandText = "Select * from Users LEFT JOIN (SELECT Id As ProfileId, UserId FROM UserProfiles) UserProfiles ON Users.Id = UserProfiles.UserId where Id = @id";
|
||||||
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
|
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
|
||||||
|
|
||||||
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
||||||
@@ -89,7 +89,7 @@ namespace Authentication
|
|||||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||||
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
||||||
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
|
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true : false;
|
||||||
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
||||||
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||||
@@ -97,9 +97,10 @@ namespace Authentication
|
|||||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||||
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
||||||
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
||||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||||
user.SecurityProfile = GetSecurityProfile(user);
|
user.SecurityProfile = GetSecurityProfile(user);
|
||||||
user.UserPreferences = GetPreferences(user);
|
user.UserPreferences = GetPreferences(user);
|
||||||
|
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
@@ -113,11 +114,11 @@ namespace Authentication
|
|||||||
public List<TUser> GetUserByName(string normalizedUserName)
|
public List<TUser> GetUserByName(string normalizedUserName)
|
||||||
{
|
{
|
||||||
List<TUser> users = new List<TUser>();
|
List<TUser> users = new List<TUser>();
|
||||||
string commandText = "Select * from Users where NormalizedEmail = @name";
|
string commandText = "Select * from Users LEFT JOIN (SELECT Id As ProfileId, UserId FROM UserProfiles) UserProfiles ON Users.Id = UserProfiles.UserId where NormalizedEmail = @name";
|
||||||
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
|
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
|
||||||
|
|
||||||
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
||||||
foreach(Dictionary<string, object> row in rows)
|
foreach (Dictionary<string, object> row in rows)
|
||||||
{
|
{
|
||||||
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
|
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
|
||||||
user.Id = (string)row["Id"];
|
user.Id = (string)row["Id"];
|
||||||
@@ -126,7 +127,7 @@ namespace Authentication
|
|||||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||||
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
||||||
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
|
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true : false;
|
||||||
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
||||||
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||||
@@ -134,9 +135,10 @@ namespace Authentication
|
|||||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||||
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
||||||
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
||||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||||
user.SecurityProfile = GetSecurityProfile(user);
|
user.SecurityProfile = GetSecurityProfile(user);
|
||||||
user.UserPreferences = GetPreferences(user);
|
user.UserPreferences = GetPreferences(user);
|
||||||
|
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||||
users.Add(user);
|
users.Add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,10 +148,10 @@ namespace Authentication
|
|||||||
public List<TUser> GetUsers()
|
public List<TUser> GetUsers()
|
||||||
{
|
{
|
||||||
List<TUser> users = new List<TUser>();
|
List<TUser> users = new List<TUser>();
|
||||||
string commandText = "Select * from Users order by NormalizedUserName";
|
string commandText = "Select * from Users LEFT JOIN (SELECT Id As ProfileId, UserId FROM UserProfiles) UserProfiles ON Users.Id = UserProfiles.UserId order by NormalizedUserName";
|
||||||
|
|
||||||
var rows = _database.ExecuteCMDDict(commandText);
|
var rows = _database.ExecuteCMDDict(commandText);
|
||||||
foreach(Dictionary<string, object> row in rows)
|
foreach (Dictionary<string, object> row in rows)
|
||||||
{
|
{
|
||||||
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
|
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
|
||||||
user.Id = (string)row["Id"];
|
user.Id = (string)row["Id"];
|
||||||
@@ -158,7 +160,7 @@ namespace Authentication
|
|||||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||||
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
|
||||||
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
|
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true : false;
|
||||||
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
|
||||||
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||||
@@ -166,9 +168,10 @@ namespace Authentication
|
|||||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||||
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
|
||||||
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
|
||||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||||
user.SecurityProfile = GetSecurityProfile(user);
|
user.SecurityProfile = GetSecurityProfile(user);
|
||||||
user.UserPreferences = GetPreferences(user);
|
user.UserPreferences = GetPreferences(user);
|
||||||
|
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||||
users.Add(user);
|
users.Add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +258,11 @@ namespace Authentication
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public int Insert(TUser user)
|
public int Insert(TUser user)
|
||||||
{
|
{
|
||||||
string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp, ConcurrencyStamp, Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, NormalizedEmail, NormalizedUserName, AccessFailedCount, LockoutEnabled, LockoutEnd, TwoFactorEnabled) values (@name, @id, @pwdHash, @SecStamp, @concurrencystamp, @email ,@emailconfirmed ,@phonenumber, @phonenumberconfirmed, @normalizedemail, @normalizedusername, @accesscount, @lockoutenabled, @lockoutenddate, @twofactorenabled);";
|
string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp, ConcurrencyStamp, Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, NormalizedEmail, NormalizedUserName, AccessFailedCount, LockoutEnabled, LockoutEnd, TwoFactorEnabled) values (@name, @id, @pwdHash, @SecStamp, @concurrencystamp, @email ,@emailconfirmed ,@phonenumber, @phonenumberconfirmed, @normalizedemail, @normalizedusername, @accesscount, @lockoutenabled, @lockoutenddate, @twofactorenabled); Insert into UserProfiles (Id, UserId, DisplayName, Quip, UnstructuredData) values (@profileId, @id, @email, '', '{}');";
|
||||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
parameters.Add("@name", user.UserName);
|
parameters.Add("@name", user.UserName);
|
||||||
parameters.Add("@id", user.Id);
|
parameters.Add("@id", user.Id);
|
||||||
|
parameters.Add("@profileId", Guid.NewGuid());
|
||||||
parameters.Add("@pwdHash", user.PasswordHash);
|
parameters.Add("@pwdHash", user.PasswordHash);
|
||||||
parameters.Add("@SecStamp", user.SecurityStamp);
|
parameters.Add("@SecStamp", user.SecurityStamp);
|
||||||
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
|
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
|
||||||
@@ -289,7 +293,7 @@ namespace Authentication
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private int Delete(string userId)
|
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 UserProfiles where UserId = @userId; Delete from GameState where UserId = @userId;";
|
||||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
parameters.Add("@userId", userId);
|
parameters.Add("@userId", userId);
|
||||||
|
|
||||||
@@ -437,5 +441,30 @@ namespace Authentication
|
|||||||
return 0;
|
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,7 +8,9 @@ namespace Authentication
|
|||||||
public List<String> Roles { get; set; }
|
public List<String> Roles { get; set; }
|
||||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||||
public string HighestRole {
|
public Guid ProfileId { get; set; }
|
||||||
|
public string HighestRole
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string _highestRole = "";
|
string _highestRole = "";
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ namespace Authentication
|
|||||||
public class SecurityProfileViewModel
|
public class SecurityProfileViewModel
|
||||||
{
|
{
|
||||||
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
|
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
|
||||||
MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult,
|
MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeGroups.AgeRestrictionGroupings.Adult,
|
||||||
IncludeUnrated = true
|
IncludeUnrated = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public class AgeRestrictionItem
|
public class AgeRestrictionItem
|
||||||
{
|
{
|
||||||
public gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; }
|
public gaseous_server.Classes.Metadata.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; }
|
||||||
public bool IncludeUnrated { get; set; }
|
public bool IncludeUnrated { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,14 @@ namespace Authentication
|
|||||||
public DateTimeOffset? LockoutEnd { get; set; }
|
public DateTimeOffset? LockoutEnd { get; set; }
|
||||||
public List<string> Roles { get; set; }
|
public List<string> Roles { get; set; }
|
||||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||||
public string HighestRole {
|
public Guid ProfileId { get; set; }
|
||||||
|
public string HighestRole
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string _highestRole = "";
|
string _highestRole = "";
|
||||||
|
if (Roles != null)
|
||||||
|
{
|
||||||
foreach (string role in Roles)
|
foreach (string role in Roles)
|
||||||
{
|
{
|
||||||
switch (role)
|
switch (role)
|
||||||
@@ -39,6 +43,11 @@ namespace Authentication
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_highestRole = "Player";
|
||||||
|
}
|
||||||
|
|
||||||
return _highestRole;
|
return _highestRole;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
@@ -11,6 +13,64 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ImportBiosFile(string FilePath, Common.hashObject Hash, ref Dictionary<string, object> BiosFileInfo)
|
||||||
|
{
|
||||||
|
BiosFileInfo.Add("type", "bios");
|
||||||
|
BiosFileInfo.Add("status", "notimported");
|
||||||
|
|
||||||
|
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
|
||||||
|
{
|
||||||
|
if (biosItem.Available == false)
|
||||||
|
{
|
||||||
|
if (biosItem.hash == Hash.md5hash)
|
||||||
|
{
|
||||||
|
string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, biosItem.hash + ".bios");
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import BIOS File", " " + FilePath + " is a BIOS file - moving to " + biosPath);
|
||||||
|
|
||||||
|
File.Move(FilePath, biosItem.biosPath, true);
|
||||||
|
|
||||||
|
BiosFileInfo.Add("name", biosItem.filename);
|
||||||
|
BiosFileInfo.Add("platform", Platforms.GetPlatform(biosItem.platformid));
|
||||||
|
BiosFileInfo["status"] = "imported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (biosItem.hash == Hash.md5hash)
|
||||||
|
{
|
||||||
|
BiosFileInfo["status"] = "duplicate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MigrateToNewFolderStructure()
|
||||||
|
{
|
||||||
|
// migrate from old BIOS file structure which had each bios file inside a folder named for the platform to the new structure which has each file in a subdirectory named after the MD5 hash
|
||||||
|
if (Directory.Exists(Config.LibraryConfiguration.LibraryBIOSDirectory))
|
||||||
|
{
|
||||||
|
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||||
|
{
|
||||||
|
if (platformMapping.Bios != null)
|
||||||
|
{
|
||||||
|
foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBiosItem in platformMapping.Bios)
|
||||||
|
{
|
||||||
|
string oldBiosPath = Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformMapping.IGDBSlug.ToString(), emulatorBiosItem.filename);
|
||||||
|
string newBiosPath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, emulatorBiosItem.hash + ".bios");
|
||||||
|
|
||||||
|
if (File.Exists(oldBiosPath))
|
||||||
|
{
|
||||||
|
File.Copy(oldBiosPath, newBiosPath, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove old BIOS folder structure
|
||||||
|
Directory.Delete(Config.LibraryConfiguration.LibraryBIOSDirectory, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Models.PlatformMapping.PlatformMapItem? BiosHashSignatureLookup(string MD5)
|
public static Models.PlatformMapping.PlatformMapItem? BiosHashSignatureLookup(string MD5)
|
||||||
{
|
{
|
||||||
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||||
@@ -67,7 +127,7 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
if (platformMapping.Bios != null)
|
if (platformMapping.Bios != null)
|
||||||
{
|
{
|
||||||
IGDB.Models.Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
|
Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
|
||||||
|
|
||||||
foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBios in platformMapping.Bios)
|
foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBios in platformMapping.Bios)
|
||||||
{
|
{
|
||||||
@@ -96,10 +156,11 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformslug, base.filename);
|
return Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool Available {
|
public bool Available
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
bool fileExists = File.Exists(biosPath);
|
bool fileExists = File.Exists(biosPath);
|
||||||
|
|||||||
@@ -7,47 +7,57 @@ using System.Security.Cryptography;
|
|||||||
using Authentication;
|
using Authentication;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using gaseous_server.Controllers;
|
using gaseous_server.Controllers;
|
||||||
|
using gaseous_server.Controllers.v1_1;
|
||||||
using gaseous_server.Models;
|
using gaseous_server.Models;
|
||||||
using IGDB.Models;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using static gaseous_server.Classes.Metadata.Games;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
public class Collections
|
public class Collections
|
||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
public static List<CollectionItem> GetCollections(string userid)
|
||||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
|
||||||
|
|
||||||
public Collections(
|
|
||||||
UserManager<ApplicationUser> userManager,
|
|
||||||
SignInManager<ApplicationUser> signInManager)
|
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
|
||||||
_signInManager = signInManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<CollectionItem> GetCollections() {
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomCollections ORDER BY `Name`";
|
string sql = "SELECT * FROM RomCollections WHERE OwnedBy=@ownedby ORDER BY `Name`";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
DataTable data = db.ExecuteCMD(sql);
|
{ "ownedby", userid }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
List<CollectionItem> collectionItems = new List<CollectionItem>();
|
List<CollectionItem> collectionItems = new List<CollectionItem>();
|
||||||
|
|
||||||
foreach(DataRow row in data.Rows) {
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
collectionItems.Add(BuildCollectionItem(row));
|
collectionItems.Add(BuildCollectionItem(row));
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectionItems;
|
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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`";
|
string sql;
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
if (userid == "")
|
||||||
dbDict.Add("id", Id);
|
{
|
||||||
|
// 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);
|
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
if (romDT.Rows.Count > 0)
|
if (romDT.Rows.Count > 0)
|
||||||
@@ -63,60 +73,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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus, 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>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("name", item.Name);
|
{
|
||||||
dbDict.Add("description", item.Description);
|
{ "name", item.Name },
|
||||||
dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())));
|
{ "description", item.Description },
|
||||||
dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())));
|
{ "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())) },
|
||||||
dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())));
|
{ "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())) },
|
||||||
dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())));
|
{ "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())) },
|
||||||
dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())));
|
{ "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())) },
|
||||||
dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1));
|
{ "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())) },
|
||||||
dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1));
|
{ "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) },
|
||||||
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
|
{ "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) },
|
||||||
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
|
{ "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) },
|
||||||
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
|
{ "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) },
|
||||||
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
|
{ "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) },
|
||||||
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
|
{ "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) },
|
||||||
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
|
{ "includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0) },
|
||||||
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
|
{ "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);
|
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||||
long CollectionId = (long)romDT.Rows[0][0];
|
long CollectionId = (long)romDT.Rows[0][0];
|
||||||
|
|
||||||
CollectionItem collectionItem = GetCollection(CollectionId);
|
CollectionItem collectionItem = GetCollection(CollectionId, userid);
|
||||||
|
|
||||||
StartCollectionItemBuild(CollectionId);
|
StartCollectionItemBuild(CollectionId, userid);
|
||||||
|
|
||||||
return collectionItem;
|
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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id";
|
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id AND OwnedBy=@ownedby";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("id", Id);
|
{
|
||||||
dbDict.Add("name", item.Name);
|
{ "id", Id },
|
||||||
dbDict.Add("description", item.Description);
|
{ "name", item.Name },
|
||||||
dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())));
|
{ "description", item.Description },
|
||||||
dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())));
|
{ "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List<long>())) },
|
||||||
dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())));
|
{ "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List<long>())) },
|
||||||
dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())));
|
{ "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List<long>())) },
|
||||||
dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())));
|
{ "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List<long>())) },
|
||||||
dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1));
|
{ "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List<long>())) },
|
||||||
dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1));
|
{ "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) },
|
||||||
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
|
{ "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) },
|
||||||
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
|
{ "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) },
|
||||||
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
|
{ "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) },
|
||||||
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
|
{ "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) },
|
||||||
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
|
{ "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) },
|
||||||
dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List<CollectionItem.AlwaysIncludeItem>())));
|
{ "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)
|
if (ForceRebuild == true)
|
||||||
{
|
{
|
||||||
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
|
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
|
||||||
@@ -139,22 +157,25 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
CollectionItem collectionItem = GetCollection(Id);
|
CollectionItem collectionItem = GetCollection(Id, userid);
|
||||||
|
|
||||||
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
|
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
|
||||||
{
|
{
|
||||||
StartCollectionItemBuild(Id);
|
StartCollectionItemBuild(Id, userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectionItem;
|
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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM RomCollections WHERE Id=@id";
|
string sql = "DELETE FROM RomCollections WHERE Id=@id AND OwnedBy=@ownedby";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("id", Id);
|
{
|
||||||
|
{ "id", Id },
|
||||||
|
{ "ownedby", userid }
|
||||||
|
};
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
||||||
@@ -164,9 +185,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)
|
if (collectionItem.BuildStatus != CollectionItem.CollectionBuildStatus.Building)
|
||||||
{
|
{
|
||||||
@@ -180,13 +202,41 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// start background task
|
// start background task
|
||||||
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 1, false, true);
|
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();
|
queueItem.ForceExecute();
|
||||||
ProcessQueue.QueueItems.Add(queueItem);
|
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>();
|
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = new List<CollectionContents.CollectionPlatformItem>();
|
||||||
|
|
||||||
// get platforms
|
// get platforms
|
||||||
@@ -214,23 +264,33 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add dynamic platforms
|
// add dynamic platforms
|
||||||
if (DynamicPlatforms.Count > 0) {
|
if (DynamicPlatforms.Count > 0)
|
||||||
foreach (long PlatformId in platformids) {
|
{
|
||||||
|
foreach (long PlatformId in platformids)
|
||||||
|
{
|
||||||
platforms.Add(Platforms.GetPlatform(PlatformId));
|
platforms.Add(Platforms.GetPlatform(PlatformId));
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// get all platforms to pull from
|
// get all platforms to pull from
|
||||||
Dictionary<string, object> FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true);
|
Dictionary<string, List<Filters.FilterItem>> FilterDict = Filters.Filter(AgeGroups.AgeRestrictionGroupings.Adult, true);
|
||||||
List<Classes.Filters.FilterItem> filteredPlatforms = (List<Classes.Filters.FilterItem>)FilterDict["platforms"];
|
List<Classes.Filters.FilterItem> filteredPlatforms = (List<Classes.Filters.FilterItem>)FilterDict["platforms"];
|
||||||
foreach (Filters.FilterItem filterItem in filteredPlatforms) {
|
foreach (Filters.FilterItem filterItem in filteredPlatforms)
|
||||||
|
{
|
||||||
platforms.Add(Platforms.GetPlatform(filterItem.Id));
|
platforms.Add(Platforms.GetPlatform(filterItem.Id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// age ratings
|
||||||
|
AgeGroups.AgeRestrictionGroupings AgeGrouping = AgeGroups.AgeRestrictionGroupings.Unclassified;
|
||||||
|
bool ContainsUnclassifiedAgeGroup = false;
|
||||||
|
|
||||||
// build collection
|
// build collection
|
||||||
List<CollectionContents.CollectionPlatformItem> platformItems = new List<CollectionContents.CollectionPlatformItem>();
|
List<CollectionContents.CollectionPlatformItem> platformItems = new List<CollectionContents.CollectionPlatformItem>();
|
||||||
|
|
||||||
foreach (Platform platform in platforms) {
|
foreach (Platform platform in platforms)
|
||||||
|
{
|
||||||
long TotalRomSize = 0;
|
long TotalRomSize = 0;
|
||||||
long TotalGameCount = 0;
|
long TotalGameCount = 0;
|
||||||
|
|
||||||
@@ -244,18 +304,32 @@ namespace gaseous_server.Classes
|
|||||||
isDynamic = true;
|
isDynamic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Game> games = new List<Game>();
|
Controllers.v1_1.GamesController.GameReturnPackage games = new Controllers.v1_1.GamesController.GameReturnPackage();
|
||||||
if (isDynamic == true)
|
if (isDynamic == true)
|
||||||
{
|
{
|
||||||
games = GamesController.GetGames("",
|
Controllers.v1_1.GamesController.GameSearchModel searchModel = new Controllers.v1_1.GamesController.GameSearchModel
|
||||||
platform.Id.ToString(),
|
{
|
||||||
string.Join(",", collectionItem.Genres),
|
Name = "",
|
||||||
string.Join(",", collectionItem.Players),
|
Platform = new List<string>{
|
||||||
string.Join(",", collectionItem.PlayerPerspectives),
|
platform.Id.ToString()
|
||||||
string.Join(",", collectionItem.Themes),
|
},
|
||||||
collectionItem.MinimumRating,
|
Genre = collectionItem.Genres.ConvertAll(s => s.ToString()),
|
||||||
collectionItem.MaximumRating
|
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);
|
CollectionContents.CollectionPlatformItem collectionPlatformItem = new CollectionContents.CollectionPlatformItem(platform);
|
||||||
@@ -271,7 +345,7 @@ namespace gaseous_server.Classes
|
|||||||
) && alwaysIncludeItem.PlatformId == platform.Id
|
) && alwaysIncludeItem.PlatformId == platform.Id
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Game AlwaysIncludeGame = Games.GetGame(alwaysIncludeItem.GameId, false, false, false);
|
MinimalGameItem AlwaysIncludeGame = new MinimalGameItem(Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, alwaysIncludeItem.GameId));
|
||||||
CollectionContents.CollectionPlatformItem.CollectionGameItem gameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(AlwaysIncludeGame);
|
CollectionContents.CollectionPlatformItem.CollectionGameItem gameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(AlwaysIncludeGame);
|
||||||
gameItem.InclusionStatus = new CollectionItem.AlwaysIncludeItem();
|
gameItem.InclusionStatus = new CollectionItem.AlwaysIncludeItem();
|
||||||
gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId;
|
gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId;
|
||||||
@@ -283,7 +357,8 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Game game in games) {
|
foreach (MinimalGameItem game in games.Games)
|
||||||
|
{
|
||||||
bool gameAlreadyInList = false;
|
bool gameAlreadyInList = false;
|
||||||
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem existingGame in collectionPlatformItem.Games)
|
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem existingGame in collectionPlatformItem.Games)
|
||||||
{
|
{
|
||||||
@@ -303,11 +378,14 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// calculate total rom size for the game
|
// calculate total rom size for the game
|
||||||
long GameRomSize = 0;
|
long GameRomSize = 0;
|
||||||
foreach (Roms.GameRomItem gameRom in gameRoms) {
|
foreach (Roms.GameRomItem gameRom in gameRoms)
|
||||||
GameRomSize += gameRom.Size;
|
{
|
||||||
|
GameRomSize += (long)gameRom.Size;
|
||||||
}
|
}
|
||||||
if (collectionItem.MaximumBytesPerPlatform > 0) {
|
if (collectionItem.MaximumBytesPerPlatform > 0)
|
||||||
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform) {
|
{
|
||||||
|
if ((TotalRomSize + GameRomSize) < collectionItem.MaximumBytesPerPlatform)
|
||||||
|
{
|
||||||
AddGame = true;
|
AddGame = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,13 +394,16 @@ namespace gaseous_server.Classes
|
|||||||
AddGame = true;
|
AddGame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AddGame == true) {
|
if (AddGame == true)
|
||||||
|
{
|
||||||
TotalRomSize += GameRomSize;
|
TotalRomSize += GameRomSize;
|
||||||
|
|
||||||
bool AddRoms = false;
|
bool AddRoms = false;
|
||||||
|
|
||||||
if (collectionItem.MaximumRomsPerPlatform > 0) {
|
if (collectionItem.MaximumRomsPerPlatform > 0)
|
||||||
if (TotalGameCount < collectionItem.MaximumRomsPerPlatform) {
|
{
|
||||||
|
if (TotalGameCount < collectionItem.MaximumRomsPerPlatform)
|
||||||
|
{
|
||||||
AddRoms = true;
|
AddRoms = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,13 +412,26 @@ namespace gaseous_server.Classes
|
|||||||
AddRoms = true;
|
AddRoms = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AddRoms == true) {
|
if (AddRoms == true)
|
||||||
|
{
|
||||||
TotalGameCount += 1;
|
TotalGameCount += 1;
|
||||||
collectionGameItem.Roms = gameRoms;
|
collectionGameItem.Roms = gameRoms;
|
||||||
collectionPlatformItem.Games.Add(collectionGameItem);
|
collectionPlatformItem.Games.Add(collectionGameItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle age grouping
|
||||||
|
List<AgeRating> gameAgeRatings = game.AgeRatings.Select(s => (AgeRating)s).ToList();
|
||||||
|
AgeGroups.AgeRestrictionGroupings CurrentAgeGroup = AgeGroups.GetAgeGroupFromAgeRatings(gameAgeRatings);
|
||||||
|
if (CurrentAgeGroup > AgeGrouping)
|
||||||
|
{
|
||||||
|
AgeGrouping = CurrentAgeGroup;
|
||||||
|
}
|
||||||
|
if (CurrentAgeGroup == AgeGroups.AgeRestrictionGroupings.Unclassified)
|
||||||
|
{
|
||||||
|
ContainsUnclassifiedAgeGroup = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collectionPlatformItem.Games.Sort((x, y) => x.Name.CompareTo(y.Name));
|
collectionPlatformItem.Games.Sort((x, y) => x.Name.CompareTo(y.Name));
|
||||||
@@ -366,30 +460,40 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
collectionPlatformItems.Sort((x, y) => x.Name.CompareTo(y.Name));
|
collectionPlatformItems.Sort((x, y) => x.Name.CompareTo(y.Name));
|
||||||
|
|
||||||
CollectionContents collectionContents = new CollectionContents();
|
CollectionContents collectionContents = new CollectionContents
|
||||||
collectionContents.Collection = collectionPlatformItems;
|
{
|
||||||
|
Collection = collectionPlatformItems,
|
||||||
|
AgeGroup = AgeGrouping,
|
||||||
|
ContainsUnclassifiedAgeGroup = ContainsUnclassifiedAgeGroup
|
||||||
|
};
|
||||||
|
|
||||||
return collectionContents;
|
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);
|
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)
|
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name);
|
Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name);
|
||||||
|
|
||||||
|
CollectionContents collectionContents = GetCollectionContent(collectionItem, userid);
|
||||||
|
|
||||||
// set starting
|
// set starting
|
||||||
string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id";
|
string sql = "UPDATE RomCollections SET BuiltStatus=@bs, AgeGroup=@ag, AgeGroupUnclassified=@agu WHERE Id=@id";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("id", collectionItem.Id);
|
{
|
||||||
dbDict.Add("bs", CollectionItem.CollectionBuildStatus.Building);
|
{ "id", collectionItem.Id },
|
||||||
|
{ "bs", CollectionItem.CollectionBuildStatus.Building },
|
||||||
|
{ "ag", collectionContents.AgeGroup },
|
||||||
|
{ "agu", collectionContents.ContainsUnclassifiedAgeGroup }
|
||||||
|
};
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = GetCollectionContent(collectionItem).Collection;
|
List<CollectionContents.CollectionPlatformItem> collectionPlatformItems = collectionContents.Collection;
|
||||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + ".zip");
|
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + collectionItem.ArchiveExtension);
|
||||||
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
|
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString());
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -418,7 +522,8 @@ namespace gaseous_server.Classes
|
|||||||
if (collectionItem.IncludeBIOSFiles == true)
|
if (collectionItem.IncludeBIOSFiles == true)
|
||||||
{
|
{
|
||||||
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
|
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
|
||||||
if (!Directory.Exists(ZipBiosPath)) {
|
if (!Directory.Exists(ZipBiosPath))
|
||||||
|
{
|
||||||
Directory.CreateDirectory(ZipBiosPath);
|
Directory.CreateDirectory(ZipBiosPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +604,7 @@ namespace gaseous_server.Classes
|
|||||||
if (File.Exists(gameRomItem.Path))
|
if (File.Exists(gameRomItem.Path))
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
|
Logging.Log(Logging.LogType.Information, "Collections", "Copying ROM: " + gameRomItem.Name);
|
||||||
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name));
|
File.Copy(gameRomItem.Path, Path.Combine(ZipGamePath, gameRomItem.Name), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,7 +613,21 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// compress to zip
|
// compress to zip
|
||||||
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
|
Logging.Log(Logging.LogType.Information, "Collections", "Compressing collection");
|
||||||
|
switch (collectionItem.ArchiveType)
|
||||||
|
{
|
||||||
|
case CollectionItem.ArchiveTypes.Zip:
|
||||||
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
|
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CollectionItem.ArchiveTypes.RAR:
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CollectionItem.ArchiveTypes.SevenZip:
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
if (Directory.Exists(ZipFileTempPath))
|
if (Directory.Exists(ZipFileTempPath))
|
||||||
@@ -543,7 +662,8 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CollectionItem BuildCollectionItem(DataRow row) {
|
private static CollectionItem BuildCollectionItem(DataRow row)
|
||||||
|
{
|
||||||
string strPlatforms = (string)Common.ReturnValueIfNull(row["Platforms"], "[ ]");
|
string strPlatforms = (string)Common.ReturnValueIfNull(row["Platforms"], "[ ]");
|
||||||
string strGenres = (string)Common.ReturnValueIfNull(row["Genres"], "[ ]");
|
string strGenres = (string)Common.ReturnValueIfNull(row["Genres"], "[ ]");
|
||||||
string strPlayers = (string)Common.ReturnValueIfNull(row["Players"], "[ ]");
|
string strPlayers = (string)Common.ReturnValueIfNull(row["Players"], "[ ]");
|
||||||
@@ -567,6 +687,7 @@ namespace gaseous_server.Classes
|
|||||||
item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1);
|
item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1);
|
||||||
item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0);
|
item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0);
|
||||||
item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"];
|
item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"];
|
||||||
|
item.ArchiveType = (CollectionItem.ArchiveTypes)(int)Common.ReturnValueIfNull(row["ArchiveType"], 0);
|
||||||
item.AlwaysInclude = Newtonsoft.Json.JsonConvert.DeserializeObject<List<CollectionItem.AlwaysIncludeItem>>(strAlwaysInclude);
|
item.AlwaysInclude = Newtonsoft.Json.JsonConvert.DeserializeObject<List<CollectionItem.AlwaysIncludeItem>>(strAlwaysInclude);
|
||||||
item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0);
|
item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0);
|
||||||
|
|
||||||
@@ -595,6 +716,32 @@ namespace gaseous_server.Classes
|
|||||||
public long? MaximumCollectionSizeInBytes { get; set; }
|
public long? MaximumCollectionSizeInBytes { get; set; }
|
||||||
public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous;
|
public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous;
|
||||||
public bool IncludeBIOSFiles { get; set; } = true;
|
public bool IncludeBIOSFiles { get; set; } = true;
|
||||||
|
public ArchiveTypes ArchiveType { get; set; } = CollectionItem.ArchiveTypes.Zip;
|
||||||
|
public string ArchiveExtension
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (ArchiveType != null)
|
||||||
|
{
|
||||||
|
switch (ArchiveType)
|
||||||
|
{
|
||||||
|
case ArchiveTypes.Zip:
|
||||||
|
default:
|
||||||
|
return ".zip";
|
||||||
|
|
||||||
|
case ArchiveTypes.RAR:
|
||||||
|
return ".rar";
|
||||||
|
|
||||||
|
case ArchiveTypes.SevenZip:
|
||||||
|
return ".7z";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ".zip";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public List<AlwaysIncludeItem> AlwaysInclude { get; set; }
|
public List<AlwaysIncludeItem> AlwaysInclude { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@@ -604,7 +751,7 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
if (_BuildStatus == CollectionBuildStatus.Completed)
|
if (_BuildStatus == CollectionBuildStatus.Completed)
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip")))
|
if (File.Exists(Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension)))
|
||||||
{
|
{
|
||||||
return CollectionBuildStatus.Completed;
|
return CollectionBuildStatus.Completed;
|
||||||
}
|
}
|
||||||
@@ -632,7 +779,7 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
if (BuildStatus == CollectionBuildStatus.Completed)
|
if (BuildStatus == CollectionBuildStatus.Completed)
|
||||||
{
|
{
|
||||||
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip");
|
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ArchiveExtension);
|
||||||
if (File.Exists(ZipFilePath))
|
if (File.Exists(ZipFilePath))
|
||||||
{
|
{
|
||||||
FileInfo fi = new FileInfo(ZipFilePath);
|
FileInfo fi = new FileInfo(ZipFilePath);
|
||||||
@@ -665,6 +812,13 @@ namespace gaseous_server.Classes
|
|||||||
RetroPie = 1
|
RetroPie = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ArchiveTypes
|
||||||
|
{
|
||||||
|
Zip = 0,
|
||||||
|
RAR = 1,
|
||||||
|
SevenZip = 2
|
||||||
|
}
|
||||||
|
|
||||||
public class AlwaysIncludeItem
|
public class AlwaysIncludeItem
|
||||||
{
|
{
|
||||||
public long PlatformId { get; set; }
|
public long PlatformId { get; set; }
|
||||||
@@ -680,7 +834,8 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CollectionContents {
|
public class CollectionContents
|
||||||
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public List<CollectionPlatformItem> Collection { get; set; }
|
public List<CollectionPlatformItem> Collection { get; set; }
|
||||||
|
|
||||||
@@ -707,13 +862,19 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CollectionPlatformItem {
|
public AgeGroups.AgeRestrictionGroupings AgeGroup { get; set; }
|
||||||
public CollectionPlatformItem(IGDB.Models.Platform platform) {
|
public bool ContainsUnclassifiedAgeGroup { get; set; }
|
||||||
|
|
||||||
|
public class CollectionPlatformItem
|
||||||
|
{
|
||||||
|
public CollectionPlatformItem(Platform platform)
|
||||||
|
{
|
||||||
string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug" };
|
string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug" };
|
||||||
|
|
||||||
PropertyInfo[] srcProperties = typeof(IGDB.Models.Platform).GetProperties();
|
PropertyInfo[] srcProperties = typeof(Platform).GetProperties();
|
||||||
PropertyInfo[] dstProperties = typeof(CollectionPlatformItem).GetProperties();
|
PropertyInfo[] dstProperties = typeof(CollectionPlatformItem).GetProperties();
|
||||||
foreach (PropertyInfo srcProperty in srcProperties) {
|
foreach (PropertyInfo srcProperty in srcProperties)
|
||||||
|
{
|
||||||
if (PropertyWhitelist.Contains<string>(srcProperty.Name))
|
if (PropertyWhitelist.Contains<string>(srcProperty.Name))
|
||||||
{
|
{
|
||||||
foreach (PropertyInfo dstProperty in dstProperties)
|
foreach (PropertyInfo dstProperty in dstProperties)
|
||||||
@@ -733,10 +894,13 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public List<CollectionGameItem> Games { get; set; }
|
public List<CollectionGameItem> Games { get; set; }
|
||||||
|
|
||||||
public int RomCount {
|
public int RomCount
|
||||||
get {
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
int Counter = 0;
|
int Counter = 0;
|
||||||
foreach (CollectionGameItem Game in Games) {
|
foreach (CollectionGameItem Game in Games)
|
||||||
|
{
|
||||||
Counter += 1;
|
Counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,12 +908,16 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RomSize {
|
public long RomSize
|
||||||
get {
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
long Size = 0;
|
long Size = 0;
|
||||||
foreach (CollectionGameItem Game in Games) {
|
foreach (CollectionGameItem Game in Games)
|
||||||
foreach (Roms.GameRomItem Rom in Game.Roms) {
|
{
|
||||||
Size += Rom.Size;
|
foreach (Roms.GameRomItem Rom in Game.Roms)
|
||||||
|
{
|
||||||
|
Size += (long)Rom.Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,51 +925,42 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CollectionGameItem {
|
public class CollectionGameItem : MinimalGameItem
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
foreach (PropertyInfo dstProperty in dstProperties)
|
public CollectionGameItem(MinimalGameItem gameObject)
|
||||||
{
|
{
|
||||||
if (srcProperty.Name == dstProperty.Name)
|
this.Id = gameObject.Id;
|
||||||
{
|
this.Name = gameObject.Name;
|
||||||
if (srcProperty.GetValue(game) != null) {
|
this.Slug = gameObject.Slug;
|
||||||
string compareName = srcProperty.PropertyType.Name.ToLower().Split("`")[0];
|
this.TotalRating = gameObject.TotalRating;
|
||||||
switch(compareName) {
|
this.TotalRatingCount = gameObject.TotalRatingCount;
|
||||||
case "identityorvalue":
|
this.Cover = gameObject.Cover;
|
||||||
string newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(srcProperty.GetValue(game));
|
this.Artworks = gameObject.Artworks;
|
||||||
Dictionary<string, object> newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
this.FirstReleaseDate = gameObject.FirstReleaseDate;
|
||||||
dstProperty.SetValue(this, newDict["Id"]);
|
this.AgeRatings = gameObject.AgeRatings;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dstProperty.SetValue(this, srcProperty.GetValue(game));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Id { get; set; }
|
public AgeGroups.AgeRestrictionGroupings AgeGrouping
|
||||||
public string Name { get; set; }
|
{
|
||||||
public string Slug { get; set; }
|
get
|
||||||
public long Cover { get; set;}
|
{
|
||||||
|
List<AgeRating> gameAgeRatings = this.AgeRatings.Select(s => (AgeRating)s).ToList();
|
||||||
|
return AgeGroups.GetAgeGroupFromAgeRatings(gameAgeRatings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CollectionItem.AlwaysIncludeItem InclusionStatus { get; set; }
|
public CollectionItem.AlwaysIncludeItem InclusionStatus { get; set; }
|
||||||
|
|
||||||
public List<Roms.GameRomItem> Roms { get; set; }
|
public List<Roms.GameRomItem> Roms { get; set; }
|
||||||
|
|
||||||
public long RomSize {
|
public long RomSize
|
||||||
get {
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
long Size = 0;
|
long Size = 0;
|
||||||
foreach (Roms.GameRomItem Rom in Roms) {
|
foreach (Roms.GameRomItem Rom in Roms)
|
||||||
Size += Rom.Size;
|
{
|
||||||
|
Size += (long)Rom.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Size;
|
return Size;
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reflection;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using static gaseous_server.Classes.Metadata.Communications;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
public class Common
|
public static class Common
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns IfNullValue if the ObjectToCheck is null
|
/// Returns IfNullValue if the ObjectToCheck is null
|
||||||
@@ -16,7 +22,8 @@ namespace gaseous_server.Classes
|
|||||||
if (ObjectToCheck == null || ObjectToCheck == System.DBNull.Value)
|
if (ObjectToCheck == null || ObjectToCheck == System.DBNull.Value)
|
||||||
{
|
{
|
||||||
return IfNullValue;
|
return IfNullValue;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return ObjectToCheck;
|
return ObjectToCheck;
|
||||||
}
|
}
|
||||||
@@ -40,11 +47,13 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
var xmlStream = File.OpenRead(FileName);
|
var xmlStream = File.OpenRead(FileName);
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Hash File", "Generating MD5 hash for file: " + FileName);
|
||||||
var md5 = MD5.Create();
|
var md5 = MD5.Create();
|
||||||
byte[] md5HashByte = md5.ComputeHash(xmlStream);
|
byte[] md5HashByte = md5.ComputeHash(xmlStream);
|
||||||
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
|
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
|
||||||
_md5hash = md5Hash;
|
_md5hash = md5Hash;
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Hash File", "Generating SHA1 hash for file: " + FileName);
|
||||||
var sha1 = SHA1.Create();
|
var sha1 = SHA1.Create();
|
||||||
xmlStream.Position = 0;
|
xmlStream.Position = 0;
|
||||||
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
|
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
|
||||||
@@ -110,6 +119,113 @@ namespace gaseous_server.Classes
|
|||||||
return Path.GetFullPath(new Uri(path).LocalPath)
|
return Path.GetFullPath(new Uri(path).LocalPath)
|
||||||
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetDescription(this Enum value)
|
||||||
|
{
|
||||||
|
return ((DescriptionAttribute)Attribute.GetCustomAttribute(
|
||||||
|
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Single(x => x.GetValue(null).Equals(value)),
|
||||||
|
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point GetResolution(this Enum value)
|
||||||
|
{
|
||||||
|
string width = ((ResolutionAttribute)Attribute.GetCustomAttribute(
|
||||||
|
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Single(x => x.GetValue(null).Equals(value)),
|
||||||
|
typeof(ResolutionAttribute)))?.width.ToString() ?? value.ToString();
|
||||||
|
|
||||||
|
string height = ((ResolutionAttribute)Attribute.GetCustomAttribute(
|
||||||
|
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Single(x => x.GetValue(null).Equals(value)),
|
||||||
|
typeof(ResolutionAttribute)))?.height.ToString() ?? value.ToString();
|
||||||
|
|
||||||
|
return new Point(int.Parse(width), int.Parse(height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsNullableEnum(this Type t)
|
||||||
|
{
|
||||||
|
Type u = Nullable.GetUnderlyingType(t);
|
||||||
|
return u != null && u.IsEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compression
|
||||||
|
public static byte[] Compress(byte[] data)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetLookupByCode(LookupTypes LookupType, string Code)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Code = @code";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "code", Code }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (int)data.Rows[0]["Id"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetLookupByValue(LookupTypes LookupType, string Value)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Value = @value";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "value", Value }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (int)data.Rows[0]["Id"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LookupTypes
|
||||||
|
{
|
||||||
|
Country,
|
||||||
|
Language
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using IGDB.Models;
|
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using NuGet.Common;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,8 @@ namespace gaseous_server.Classes
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
string logPath = Path.Combine(ConfigurationPath, "Logs");
|
string logPath = Path.Combine(ConfigurationPath, "Logs");
|
||||||
if (!Directory.Exists(logPath)) {
|
if (!Directory.Exists(logPath))
|
||||||
|
{
|
||||||
Directory.CreateDirectory(logPath);
|
Directory.CreateDirectory(logPath);
|
||||||
}
|
}
|
||||||
return logPath;
|
return logPath;
|
||||||
@@ -113,15 +114,42 @@ namespace gaseous_server.Classes
|
|||||||
if (File.Exists(ConfigurationFilePath))
|
if (File.Exists(ConfigurationFilePath))
|
||||||
{
|
{
|
||||||
string configRaw = File.ReadAllText(ConfigurationFilePath);
|
string configRaw = File.ReadAllText(ConfigurationFilePath);
|
||||||
ConfigFile? _tempConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<ConfigFile>(configRaw);
|
Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings
|
||||||
|
{
|
||||||
|
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
|
||||||
|
MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore
|
||||||
|
};
|
||||||
|
ConfigFile? _tempConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<ConfigFile>(configRaw, serializerSettings);
|
||||||
if (_tempConfig != null)
|
if (_tempConfig != null)
|
||||||
{
|
{
|
||||||
_config = _tempConfig;
|
_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.DefaultMetadataSource = (HasheousClient.Models.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataSources), (string)Common.GetEnvVar("metadatasource", _config.MetadataConfiguration.DefaultMetadataSource.ToString()));
|
||||||
|
_config.IGDBConfiguration.UseHasheousProxy = bool.Parse((string)Common.GetEnvVar("metadatausehasheousproxy", _config.IGDBConfiguration.UseHasheousProxy.ToString()));
|
||||||
|
_config.MetadataConfiguration.SignatureSource = (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), (string)Common.GetEnvVar("signaturesource", _config.MetadataConfiguration.SignatureSource.ToString())); ;
|
||||||
|
_config.MetadataConfiguration.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");
|
throw new Exception("There was an error reading the config file: Json returned null");
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// no config file!
|
// no config file!
|
||||||
// use defaults and save
|
// use defaults and save
|
||||||
@@ -130,8 +158,8 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Using configuration:");
|
// Console.WriteLine("Using configuration:");
|
||||||
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(_config, Formatting.Indented));
|
// Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(_config, Formatting.Indented));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UpdateConfig()
|
public static void UpdateConfig()
|
||||||
@@ -161,7 +189,7 @@ namespace gaseous_server.Classes
|
|||||||
File.WriteAllText(ConfigurationFilePath, configRaw);
|
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()
|
public static void InitSettings()
|
||||||
{
|
{
|
||||||
@@ -171,45 +199,122 @@ namespace gaseous_server.Classes
|
|||||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
DataTable dbResponse = db.ExecuteCMD(sql);
|
||||||
foreach (DataRow dataRow in dbResponse.Rows)
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Database.schema_version >= 1016)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AppSettings.Add((string)dataRow["Setting"], (string)dataRow["Value"]);
|
AppSettings.Add(SettingName, dataRow["Value"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))
|
if (AppSettings.ContainsKey(SettingName))
|
||||||
{
|
{
|
||||||
return AppSettings[SettingName];
|
return (T)AppSettings[SettingName];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
string sql;
|
||||||
string sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
{
|
||||||
dbDict.Add("SettingName", SettingName);
|
{ "SettingName", SettingName }
|
||||||
dbDict.Add("Value", DefaultValue);
|
};
|
||||||
|
DataTable dbResponse;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Reading setting '" + SettingName + "'");
|
Logging.Log(Logging.LogType.Debug, "Database", "Reading setting '" + SettingName + "'");
|
||||||
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
|
|
||||||
|
if (Database.schema_version >= 1016)
|
||||||
|
{
|
||||||
|
sql = "SELECT Value, ValueDate FROM Settings WHERE Setting = @SettingName";
|
||||||
|
|
||||||
|
dbResponse = db.ExecuteCMD(sql, dbDict);
|
||||||
|
Type type = typeof(T);
|
||||||
if (dbResponse.Rows.Count == 0)
|
if (dbResponse.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// no value with that name stored - respond with the default value
|
// no value with that name stored - respond with the default value
|
||||||
SetSetting(SettingName, DefaultValue);
|
SetSetting<T>(SettingName, DefaultValue);
|
||||||
return DefaultValue;
|
return DefaultValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AppSettings.Add(SettingName, (string)dbResponse.Rows[0][0]);
|
if (type.ToString() == "System.DateTime")
|
||||||
return (string)dbResponse.Rows[0][0];
|
{
|
||||||
|
AppSettings.Add(SettingName, (T)dbResponse.Rows[0]["ValueDate"]);
|
||||||
|
return (T)dbResponse.Rows[0]["ValueDate"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppSettings.Add(SettingName, (T)dbResponse.Rows[0]["Value"]);
|
||||||
|
return (T)dbResponse.Rows[0]["Value"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
|
||||||
|
|
||||||
|
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<T>(SettingName, DefaultValue);
|
||||||
|
return DefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppSettings.Add(SettingName, (T)dbResponse.Rows[0]["Value"]);
|
||||||
|
return (T)dbResponse.Rows[0]["Value"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -219,14 +324,72 @@ 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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "REPLACE INTO Settings (Setting, Value) VALUES (@SettingName, @Value)";
|
string sql;
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict;
|
||||||
dbDict.Add("SettingName", SettingName);
|
|
||||||
dbDict.Add("Value", Value);
|
if (Database.schema_version >= 1016)
|
||||||
|
{
|
||||||
|
sql = "REPLACE INTO Settings (Setting, ValueType, Value, ValueDate) VALUES (@SettingName, @ValueType, @Value, @ValueDate)";
|
||||||
|
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 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sql = "REPLACE INTO Settings (Setting, Value) VALUES (@SettingName, @Value)";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "SettingName", SettingName },
|
||||||
|
{ "Value", Value }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Storing setting '" + SettingName + "' to value: '" + Value + "'");
|
Logging.Log(Logging.LogType.Debug, "Database", "Storing setting '" + SettingName + "' to value: '" + Value + "'");
|
||||||
try
|
try
|
||||||
@@ -264,7 +427,8 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public class Database
|
public class Database
|
||||||
{
|
{
|
||||||
private static string _DefaultHostName {
|
private static string _DefaultHostName
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbhost")))
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbhost")))
|
||||||
@@ -308,11 +472,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 HostName = _DefaultHostName;
|
||||||
public string UserName = _DefaultUserName;
|
public string UserName = _DefaultUserName;
|
||||||
public string Password = _DefaultPassword;
|
public string Password = _DefaultPassword;
|
||||||
public string DatabaseName = "gaseous";
|
public string DatabaseName = _DefaultDatabaseName;
|
||||||
public int Port = 3306;
|
public int Port = _DefaultDatabasePort;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string ConnectionString
|
public string ConnectionString
|
||||||
@@ -341,11 +535,7 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return ReadSetting("LibraryRootDirectory", Path.Combine(Config.ConfigurationPath, "Data"));
|
return Path.Combine(Config.ConfigurationPath, "Data");
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
SetSetting("LibraryRootDirectory", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,6 +579,14 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string LibraryFirmwareDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Path.Combine(LibraryRootDirectory, "Firmware");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string LibraryUploadDirectory
|
public string LibraryUploadDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -429,28 +627,35 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LibraryMetadataDirectory_Platform(Platform platform)
|
public string LibraryMetadataDirectory_Platform(HasheousClient.Models.Metadata.IGDB.Platform platform)
|
||||||
{
|
{
|
||||||
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Platforms", platform.Slug);
|
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Platforms", platform.Slug);
|
||||||
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
||||||
return MetadataPath;
|
return MetadataPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LibraryMetadataDirectory_Game(Game game)
|
public string LibraryMetadataDirectory_Game(gaseous_server.Models.Game game)
|
||||||
{
|
{
|
||||||
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Games", game.Slug);
|
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Games", game.Slug);
|
||||||
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
||||||
return MetadataPath;
|
return MetadataPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LibraryMetadataDirectory_Company(Company company)
|
public string LibraryMetadataDirectory_Company(HasheousClient.Models.Metadata.IGDB.Company company)
|
||||||
{
|
{
|
||||||
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Companies", company.Slug);
|
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Companies", company.Slug);
|
||||||
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
||||||
return MetadataPath;
|
return MetadataPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LibrarySignatureImportDirectory
|
public string LibraryMetadataDirectory_Hasheous()
|
||||||
|
{
|
||||||
|
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Hasheous");
|
||||||
|
if (!Directory.Exists(MetadataPath)) { Directory.CreateDirectory(MetadataPath); }
|
||||||
|
return MetadataPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LibrarySignaturesDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -458,37 +663,114 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string LibrarySignaturesProcessedDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Path.Combine(LibraryRootDirectory, "Signatures - Processed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void InitLibrary()
|
public void InitLibrary()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
|
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
|
||||||
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
|
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
|
||||||
if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
|
if (!Directory.Exists(LibraryFirmwareDirectory)) { Directory.CreateDirectory(LibraryFirmwareDirectory); }
|
||||||
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
|
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
|
||||||
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
|
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
|
||||||
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
|
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
|
||||||
if (!Directory.Exists(LibraryCollectionsDirectory)) { Directory.CreateDirectory(LibraryCollectionsDirectory); }
|
if (!Directory.Exists(LibraryCollectionsDirectory)) { Directory.CreateDirectory(LibraryCollectionsDirectory); }
|
||||||
if (!Directory.Exists(LibrarySignatureImportDirectory)) { Directory.CreateDirectory(LibrarySignatureImportDirectory); }
|
if (!Directory.Exists(LibrarySignaturesDirectory)) { Directory.CreateDirectory(LibrarySignaturesDirectory); }
|
||||||
|
if (!Directory.Exists(LibrarySignaturesProcessedDirectory)) { Directory.CreateDirectory(LibrarySignaturesProcessedDirectory); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MetadataAPI
|
public class MetadataAPI
|
||||||
{
|
{
|
||||||
private static Communications.MetadataSources _Source
|
public static string _HasheousClientAPIKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "Pna5SRcbJ6R8aasytab_6vZD0aBKDGNZKRz4WY4xArpfZ-3mdZq0hXIGyy0AD43b";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HasheousClient.Models.MetadataSources _MetadataSource
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatasource")))
|
||||||
{
|
{
|
||||||
return (Communications.MetadataSources)Enum.Parse(typeof(Communications.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
|
return (HasheousClient.Models.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataSources), Environment.GetEnvironmentVariable("metadatasource"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Communications.MetadataSources.IGDB;
|
return HasheousClient.Models.MetadataSources.IGDB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Communications.MetadataSources Source = _Source;
|
private static bool _MetadataUseHasheousProxy
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("metadatausehasheousproxy")))
|
||||||
|
{
|
||||||
|
return bool.Parse(Environment.GetEnvironmentVariable("metadatausehasheousproxy"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bool _HasheousSubmitFixes { get; set; } = false;
|
||||||
|
|
||||||
|
private static string _HasheousAPIKey { get; set; } = "";
|
||||||
|
|
||||||
|
private static string _HasheousHost
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("hasheoushost")))
|
||||||
|
{
|
||||||
|
return Environment.GetEnvironmentVariable("hasheoushost");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "https://hasheous.org/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HasheousClient.Models.MetadataSources DefaultMetadataSource = _MetadataSource;
|
||||||
|
|
||||||
|
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
|
||||||
|
|
||||||
|
public bool HasheousSubmitFixes = _HasheousSubmitFixes;
|
||||||
|
|
||||||
|
public string HasheousAPIKey = _HasheousAPIKey;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string HasheousClientAPIKey = _HasheousClientAPIKey;
|
||||||
|
|
||||||
|
public string HasheousHost = _HasheousHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IGDB
|
public class IGDB
|
||||||
@@ -523,8 +805,24 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool _MetadataUseHasheousProxy
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("igdbusehasheousproxy")))
|
||||||
|
{
|
||||||
|
return bool.Parse(Environment.GetEnvironmentVariable("igdbusehasheousproxy"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string ClientId = _DefaultIGDBClientId;
|
public string ClientId = _DefaultIGDBClientId;
|
||||||
public string Secret = _DefaultIGDBSecret;
|
public string Secret = _DefaultIGDBSecret;
|
||||||
|
public bool UseHasheousProxy = _MetadataUseHasheousProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Logging
|
public class Logging
|
||||||
|
|||||||
@@ -9,6 +9,21 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
public class Database
|
public class Database
|
||||||
{
|
{
|
||||||
|
private static int _schema_version { get; set; } = 0;
|
||||||
|
public static int schema_version
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
//Logging.Log(Logging.LogType.Information, "Database Schema", "Schema version is " + _schema_version);
|
||||||
|
return _schema_version;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
//Logging.Log(Logging.LogType.Information, "Database Schema", "Setting schema version " + _schema_version);
|
||||||
|
_schema_version = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Database()
|
public Database()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -58,6 +73,8 @@ namespace gaseous_server.Classes
|
|||||||
// load resources
|
// load resources
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
||||||
|
DatabaseMemoryCacheOptions? CacheOptions = new DatabaseMemoryCacheOptions(false);
|
||||||
|
|
||||||
switch (_ConnectorType)
|
switch (_ConnectorType)
|
||||||
{
|
{
|
||||||
case databaseType.MySql:
|
case databaseType.MySql:
|
||||||
@@ -65,20 +82,29 @@ namespace gaseous_server.Classes
|
|||||||
string sql = "CREATE DATABASE IF NOT EXISTS `" + Config.DatabaseConfiguration.DatabaseName + "`;";
|
string sql = "CREATE DATABASE IF NOT EXISTS `" + Config.DatabaseConfiguration.DatabaseName + "`;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
Logging.Log(Logging.LogType.Information, "Database", "Creating database if it doesn't exist");
|
Logging.Log(Logging.LogType.Information, "Database", "Creating database if it doesn't exist");
|
||||||
ExecuteCMD(sql, dbDict, 30, "server=" + Config.DatabaseConfiguration.HostName + ";port=" + Config.DatabaseConfiguration.Port + ";userid=" + Config.DatabaseConfiguration.UserName + ";password=" + Config.DatabaseConfiguration.Password);
|
ExecuteCMD(sql, dbDict, CacheOptions, 30, "server=" + Config.DatabaseConfiguration.HostName + ";port=" + Config.DatabaseConfiguration.Port + ";userid=" + Config.DatabaseConfiguration.UserName + ";password=" + Config.DatabaseConfiguration.Password);
|
||||||
|
|
||||||
// check if schema version table is in place - if not, create the schema version table
|
// check if schema version table is in place - if not, create the schema version table
|
||||||
sql = "SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '" + Config.DatabaseConfiguration.DatabaseName + "' AND TABLE_NAME = 'schema_version';";
|
sql = "SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '" + Config.DatabaseConfiguration.DatabaseName + "' AND TABLE_NAME = 'schema_version';";
|
||||||
DataTable SchemaVersionPresent = ExecuteCMD(sql, dbDict);
|
DataTable SchemaVersionPresent = ExecuteCMD(sql, dbDict, CacheOptions);
|
||||||
if (SchemaVersionPresent.Rows.Count == 0)
|
if (SchemaVersionPresent.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// no schema table present - create it
|
// no schema table present - create it
|
||||||
Logging.Log(Logging.LogType.Information, "Database", "Schema version table doesn't exist. Creating it.");
|
Logging.Log(Logging.LogType.Information, "Database", "Schema version table doesn't exist. Creating it.");
|
||||||
sql = "CREATE TABLE `schema_version` (`schema_version` INT NOT NULL, PRIMARY KEY (`schema_version`)); INSERT INTO `schema_version` (`schema_version`) VALUES (0);";
|
sql = "CREATE TABLE `schema_version` (`schema_version` INT NOT NULL, PRIMARY KEY (`schema_version`)); INSERT INTO `schema_version` (`schema_version`) VALUES (0);";
|
||||||
ExecuteCMD(sql, dbDict);
|
ExecuteCMD(sql, dbDict, CacheOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1000; i < 10000; i++)
|
sql = "SELECT schema_version FROM schema_version;";
|
||||||
|
dbDict = new Dictionary<string, object>();
|
||||||
|
DataTable SchemaVersion = ExecuteCMD(sql, dbDict, CacheOptions);
|
||||||
|
int OuterSchemaVer = (int)SchemaVersion.Rows[0][0];
|
||||||
|
if (OuterSchemaVer == 0)
|
||||||
|
{
|
||||||
|
OuterSchemaVer = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = OuterSchemaVer; i < 10000; i++)
|
||||||
{
|
{
|
||||||
string resourceName = "gaseous_server.Support.Database.MySQL.gaseous-" + i + ".sql";
|
string resourceName = "gaseous_server.Support.Database.MySQL.gaseous-" + i + ".sql";
|
||||||
string dbScript = "";
|
string dbScript = "";
|
||||||
@@ -94,7 +120,7 @@ namespace gaseous_server.Classes
|
|||||||
// apply script
|
// apply script
|
||||||
sql = "SELECT schema_version FROM schema_version;";
|
sql = "SELECT schema_version FROM schema_version;";
|
||||||
dbDict = new Dictionary<string, object>();
|
dbDict = new Dictionary<string, object>();
|
||||||
DataTable SchemaVersion = ExecuteCMD(sql, dbDict);
|
SchemaVersion = ExecuteCMD(sql, dbDict, CacheOptions);
|
||||||
if (SchemaVersion.Rows.Count == 0)
|
if (SchemaVersion.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// something is broken here... where's the table?
|
// something is broken here... where's the table?
|
||||||
@@ -105,22 +131,35 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
int SchemaVer = (int)SchemaVersion.Rows[0][0];
|
int SchemaVer = (int)SchemaVersion.Rows[0][0];
|
||||||
Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer);
|
Logging.Log(Logging.LogType.Information, "Database", "Schema version is " + SchemaVer);
|
||||||
|
// update schema version variable
|
||||||
|
Database.schema_version = SchemaVer;
|
||||||
if (SchemaVer < i)
|
if (SchemaVer < i)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// run pre-upgrade code
|
// run pre-upgrade code
|
||||||
DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
|
DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
|
||||||
|
|
||||||
// apply schema!
|
// apply schema!
|
||||||
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
|
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
|
||||||
ExecuteCMD(dbScript, dbDict);
|
ExecuteCMD(dbScript, dbDict, CacheOptions, 180);
|
||||||
|
|
||||||
sql = "UPDATE schema_version SET schema_version=@schemaver";
|
sql = "UPDATE schema_version SET schema_version=@schemaver";
|
||||||
dbDict = new Dictionary<string, object>();
|
dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("schemaver", i);
|
dbDict.Add("schemaver", i);
|
||||||
ExecuteCMD(sql, dbDict);
|
ExecuteCMD(sql, dbDict, CacheOptions);
|
||||||
|
|
||||||
// run post-upgrade code
|
// run post-upgrade code
|
||||||
DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
|
DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
|
||||||
|
|
||||||
|
// update schema version variable
|
||||||
|
Database.schema_version = i;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Critical, "Database", "Schema upgrade failed! Unable to continue.", ex);
|
||||||
|
System.Environment.Exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,39 +172,83 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public DataTable ExecuteCMD(string Command)
|
public DataTable ExecuteCMD(string Command)
|
||||||
{
|
{
|
||||||
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
return _ExecuteCMD(Command, dbDict, 30, "");
|
return _ExecuteCMD(Command, dbDict, CacheOptions, 30, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataTable ExecuteCMD(string Command, DatabaseMemoryCacheOptions? CacheOptions)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
return _ExecuteCMD(Command, dbDict, CacheOptions, 30, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters)
|
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters)
|
||||||
{
|
{
|
||||||
return _ExecuteCMD(Command, Parameters, 30, "");
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
|
return _ExecuteCMD(Command, Parameters, CacheOptions, 30, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions)
|
||||||
|
{
|
||||||
|
return _ExecuteCMD(Command, Parameters, CacheOptions, 30, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||||
{
|
{
|
||||||
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
|
return _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||||
|
{
|
||||||
|
return _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command)
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command)
|
||||||
{
|
{
|
||||||
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
return _ExecuteCMDDict(Command, dbDict, 30, "");
|
return _ExecuteCMDDict(Command, dbDict, CacheOptions, 30, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, DatabaseMemoryCacheOptions? CacheOptions)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
return _ExecuteCMDDict(Command, dbDict, CacheOptions, 30, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters)
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters)
|
||||||
{
|
{
|
||||||
return _ExecuteCMDDict(Command, Parameters, 30, "");
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
|
return _ExecuteCMDDict(Command, Parameters, CacheOptions, 30, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions)
|
||||||
|
{
|
||||||
|
return _ExecuteCMDDict(Command, Parameters, CacheOptions, 30, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||||
{
|
{
|
||||||
return _ExecuteCMDDict(Command, Parameters, Timeout, ConnectionString);
|
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||||
|
|
||||||
|
return _ExecuteCMDDict(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||||
{
|
{
|
||||||
DataTable dataTable = _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
return _ExecuteCMDDict(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||||
|
{
|
||||||
|
DataTable dataTable = _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||||
|
|
||||||
// convert datatable to dictionary
|
// convert datatable to dictionary
|
||||||
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
|
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
|
||||||
@@ -191,14 +274,45 @@ namespace gaseous_server.Classes
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||||
{
|
{
|
||||||
|
string CacheKey = Command + string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
|
||||||
|
|
||||||
|
if (CacheOptions is object && CacheOptions.CacheEnabled)
|
||||||
|
{
|
||||||
|
object? CachedData = DatabaseMemoryCache.GetCacheObject(CacheKey);
|
||||||
|
if (CachedData is object)
|
||||||
|
{
|
||||||
|
return (DataTable)CachedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// purge cache if command contains "INSERT", "UPDATE", "DELETE", or "ALTER"
|
||||||
|
if (
|
||||||
|
Command.Contains("INSERT", StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
Command.Contains("UPDATE", StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
Command.Contains("DELETE", StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
Command.Contains("ALTER", StringComparison.InvariantCultureIgnoreCase)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// exclude logging events from purging the cache
|
||||||
|
if (!Command.StartsWith("INSERT INTO SERVERLOGS", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
DatabaseMemoryCache.ClearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
||||||
switch (_ConnectorType)
|
switch (_ConnectorType)
|
||||||
{
|
{
|
||||||
case databaseType.MySql:
|
case databaseType.MySql:
|
||||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||||
return (DataTable)conn.ExecCMD(Command, Parameters, Timeout);
|
DataTable RetTable = conn.ExecCMD(Command, Parameters, Timeout);
|
||||||
|
if (CacheOptions is object && CacheOptions.CacheEnabled)
|
||||||
|
{
|
||||||
|
DatabaseMemoryCache.SetCacheObject(CacheKey, RetTable, CacheOptions.ExpirationSeconds);
|
||||||
|
}
|
||||||
|
return RetTable;
|
||||||
default:
|
default:
|
||||||
return new DataTable();
|
return new DataTable();
|
||||||
}
|
}
|
||||||
@@ -227,7 +341,8 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
case databaseType.MySql:
|
case databaseType.MySql:
|
||||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||||
return (int)conn.ExecNonQuery(Command, Parameters, Timeout);
|
int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
|
||||||
|
return retVal;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -256,6 +371,148 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class DatabaseMemoryCache
|
||||||
|
{
|
||||||
|
private class MemoryCacheItem
|
||||||
|
{
|
||||||
|
public MemoryCacheItem(object CacheObject)
|
||||||
|
{
|
||||||
|
cacheObject = CacheObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryCacheItem(object CacheObject, int ExpirationSeconds)
|
||||||
|
{
|
||||||
|
cacheObject = CacheObject;
|
||||||
|
expirationSeconds = ExpirationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time the object was added to the cache in ticks
|
||||||
|
/// </summary>
|
||||||
|
public long addedTime { get; } = Environment.TickCount64;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time the object will expire in ticks
|
||||||
|
/// </summary>
|
||||||
|
public long expirationTime
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return addedTime + _expirationTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of seconds the object will be cached
|
||||||
|
/// </summary>
|
||||||
|
public int expirationSeconds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (int)TimeSpan.FromTicks(_expirationTicks).Seconds;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_expirationTicks = (long)TimeSpan.FromSeconds(value).Ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long _expirationTicks = (long)TimeSpan.FromSeconds(2).Ticks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The object to be cached
|
||||||
|
/// </summary>
|
||||||
|
public object cacheObject { get; set; }
|
||||||
|
}
|
||||||
|
private static Dictionary<string, MemoryCacheItem> MemoryCache = new Dictionary<string, MemoryCacheItem>();
|
||||||
|
private static Timer CacheTimer = new Timer(CacheTimerCallback, null, 0, 1000);
|
||||||
|
|
||||||
|
private static void CacheTimerCallback(object? state)
|
||||||
|
{
|
||||||
|
ClearExpiredCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object? GetCacheObject(string CacheKey)
|
||||||
|
{
|
||||||
|
if (MemoryCache.ContainsKey(CacheKey))
|
||||||
|
{
|
||||||
|
if (MemoryCache[CacheKey].expirationTime < Environment.TickCount)
|
||||||
|
{
|
||||||
|
MemoryCache.Remove(CacheKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MemoryCache[CacheKey].cacheObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void SetCacheObject(string CacheKey, object CacheObject, int ExpirationSeconds = 2)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (MemoryCache.ContainsKey(CacheKey))
|
||||||
|
{
|
||||||
|
MemoryCache.Remove(CacheKey);
|
||||||
|
}
|
||||||
|
MemoryCache.Add(CacheKey, new MemoryCacheItem(CacheObject, ExpirationSeconds));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Debug, "Database", "Error while setting cache object", ex);
|
||||||
|
ClearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void RemoveCacheObject(string CacheKey)
|
||||||
|
{
|
||||||
|
if (MemoryCache.ContainsKey(CacheKey))
|
||||||
|
{
|
||||||
|
MemoryCache.Remove(CacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void ClearCache()
|
||||||
|
{
|
||||||
|
MemoryCache.Clear();
|
||||||
|
}
|
||||||
|
private static void ClearExpiredCache()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
long currTime = Environment.TickCount64;
|
||||||
|
|
||||||
|
Dictionary<string, MemoryCacheItem> ExpiredItems = MemoryCache;
|
||||||
|
foreach (string key in ExpiredItems.Keys)
|
||||||
|
{
|
||||||
|
if (MemoryCache[key].expirationTime < currTime)
|
||||||
|
{
|
||||||
|
Console.WriteLine("\x1b[95mPurging expired cache item " + key + ". Added: " + MemoryCache[key].addedTime + ". Expired: " + MemoryCache[key].expirationTime);
|
||||||
|
MemoryCache.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Debug, "Database", "Error while clearing expired cache", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DatabaseMemoryCacheOptions
|
||||||
|
{
|
||||||
|
public DatabaseMemoryCacheOptions(bool CacheEnabled = false, int ExpirationSeconds = 1)
|
||||||
|
{
|
||||||
|
this.CacheEnabled = CacheEnabled;
|
||||||
|
this.ExpirationSeconds = ExpirationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CacheEnabled { get; set; }
|
||||||
|
public int ExpirationSeconds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public int GetDatabaseSchemaVersion()
|
public int GetDatabaseSchemaVersion()
|
||||||
{
|
{
|
||||||
switch (_ConnectorType)
|
switch (_ConnectorType)
|
||||||
@@ -321,7 +578,8 @@ namespace gaseous_server.Classes
|
|||||||
DataTable RetTable = new DataTable();
|
DataTable RetTable = new DataTable();
|
||||||
|
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||||
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
MySqlCommand cmd = new MySqlCommand
|
MySqlCommand cmd = new MySqlCommand
|
||||||
@@ -345,7 +603,9 @@ namespace gaseous_server.Classes
|
|||||||
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
||||||
}
|
}
|
||||||
RetTable.Load(cmd.ExecuteReader());
|
RetTable.Load(cmd.ExecuteReader());
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
||||||
Trace.WriteLine("Error executing " + SQL);
|
Trace.WriteLine("Error executing " + SQL);
|
||||||
Trace.WriteLine("Full exception: " + ex.ToString());
|
Trace.WriteLine("Full exception: " + ex.ToString());
|
||||||
@@ -353,16 +613,18 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
||||||
conn.Close();
|
conn.Close();
|
||||||
|
}
|
||||||
|
|
||||||
return RetTable;
|
return RetTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ExecNonQuery(string SQL, Dictionary< string, object> Parameters, int Timeout)
|
public int ExecNonQuery(string SQL, Dictionary<string, object> Parameters, int Timeout)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||||
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
MySqlCommand cmd = new MySqlCommand
|
MySqlCommand cmd = new MySqlCommand
|
||||||
@@ -386,7 +648,9 @@ namespace gaseous_server.Classes
|
|||||||
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
||||||
}
|
}
|
||||||
result = cmd.ExecuteNonQuery();
|
result = cmd.ExecuteNonQuery();
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
||||||
Trace.WriteLine("Error executing " + SQL);
|
Trace.WriteLine("Error executing " + SQL);
|
||||||
Trace.WriteLine("Full exception: " + ex.ToString());
|
Trace.WriteLine("Full exception: " + ex.ToString());
|
||||||
@@ -394,13 +658,15 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
|
||||||
conn.Close();
|
conn.Close();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
|
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
|
||||||
{
|
{
|
||||||
var conn = new MySqlConnection(DBConn);
|
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||||
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
var command = conn.CreateCommand();
|
var command = conn.CreateCommand();
|
||||||
MySqlTransaction transaction;
|
MySqlTransaction transaction;
|
||||||
@@ -417,6 +683,7 @@ namespace gaseous_server.Classes
|
|||||||
transaction.Commit();
|
transaction.Commit();
|
||||||
conn.Close();
|
conn.Close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
|
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
|
||||||
{
|
{
|
||||||
@@ -441,7 +708,8 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public bool TestConnection()
|
public bool TestConnection()
|
||||||
{
|
{
|
||||||
MySqlConnection conn = new MySqlConnection(DBConn);
|
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
@@ -455,5 +723,6 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
@@ -9,15 +12,68 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
|
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)
|
public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
switch(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)
|
||||||
{
|
{
|
||||||
case Database.databaseType.MySql:
|
case Database.databaseType.MySql:
|
||||||
switch (TargetSchemaVersion)
|
switch (TargetSchemaVersion)
|
||||||
@@ -32,12 +88,12 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// copy root path to new libraries format
|
// copy root path to new libraries format
|
||||||
string oldRoot = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Library");
|
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("name", "Default");
|
||||||
dbDict.Add("path", oldRoot);
|
dbDict.Add("path", oldRoot);
|
||||||
dbDict.Add("defaultlibrary", 1);
|
dbDict.Add("defaultlibrary", 1);
|
||||||
dbDict.Add("defaultplatform", 0);
|
dbDict.Add("defaultplatform", 0);
|
||||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
// apply the new library id to the existing roms
|
// apply the new library id to the existing roms
|
||||||
sql = "UPDATE Games_Roms SET LibraryId=@libraryid;";
|
sql = "UPDATE Games_Roms SET LibraryId=@libraryid;";
|
||||||
@@ -46,6 +102,166 @@ namespace gaseous_server.Classes
|
|||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 1016:
|
||||||
|
// delete old format LastRun_* settings from settings table
|
||||||
|
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
|
||||||
|
db.ExecuteNonQuery(sql);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1023:
|
||||||
|
// load country list
|
||||||
|
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding country look up table contents");
|
||||||
|
|
||||||
|
string countryResourceName = "gaseous_server.Support.Country.txt";
|
||||||
|
using (Stream stream = assembly.GetManifestResourceStream(countryResourceName))
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
string[] line = reader.ReadLine().Split("|");
|
||||||
|
|
||||||
|
sql = "INSERT INTO Country (Code, Value) VALUES (@code, @value);";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "code", line[0] },
|
||||||
|
{ "value", line[1] }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
} while (reader.EndOfStream == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load language list
|
||||||
|
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding language look up table contents");
|
||||||
|
|
||||||
|
string languageResourceName = "gaseous_server.Support.Language.txt";
|
||||||
|
using (Stream stream = assembly.GetManifestResourceStream(languageResourceName))
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
string[] line = reader.ReadLine().Split("|");
|
||||||
|
|
||||||
|
sql = "INSERT INTO Language (Code, Value) VALUES (@code, @value);";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "code", line[0] },
|
||||||
|
{ "value", line[1] }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
} while (reader.EndOfStream == false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1024:
|
||||||
|
// create profiles for all existing users
|
||||||
|
sql = "SELECT * FROM Users;";
|
||||||
|
data = db.ExecuteCMD(sql);
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
// get legacy avatar from UserAvatars table
|
||||||
|
sql = "SELECT Avatar FROM UserAvatars WHERE UserId = @userid;";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "userid", row["Id"] }
|
||||||
|
};
|
||||||
|
DataTable avatarData = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
sql = "INSERT INTO UserProfiles (Id, UserId, DisplayName, Quip, Avatar, AvatarExtension, UnstructuredData) VALUES (@id, @userid, @displayname, @quip, @avatar, @avatarextension, @data);";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", Guid.NewGuid() },
|
||||||
|
{ "userid", row["Id"] },
|
||||||
|
{ "displayname", row["Email"] },
|
||||||
|
{ "quip", "" },
|
||||||
|
{ "avatar", avatarData.Rows.Count > 0 ? avatarData.Rows[0]["Avatar"] : null },
|
||||||
|
{ "avatarextension", avatarData.Rows.Count > 0 ? ".jpg" : null },
|
||||||
|
{ "data", "{}" }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all rom paths to use the new format
|
||||||
|
sql = "SELECT * FROM GameLibraries;";
|
||||||
|
data = db.ExecuteCMD(sql);
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid;";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "libraryid", row["Id"] }
|
||||||
|
};
|
||||||
|
DataTable romData = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
string libraryRootPath = (string)row["Path"];
|
||||||
|
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
|
||||||
|
{
|
||||||
|
libraryRootPath += Path.DirectorySeparatorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetLastThreeElements = false;
|
||||||
|
if ((int)row["DefaultLibrary"] == 1)
|
||||||
|
{
|
||||||
|
GetLastThreeElements = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (DataRow romRow in romData.Rows)
|
||||||
|
{
|
||||||
|
string existingPath = (string)romRow["RelativePath"];
|
||||||
|
string newPath = "";
|
||||||
|
|
||||||
|
if (GetLastThreeElements == true)
|
||||||
|
{
|
||||||
|
// strip all but the last 3 elements from existingPath separated by directory separator
|
||||||
|
// this mode only works for the default library
|
||||||
|
string[] pathParts = existingPath.Split(Path.DirectorySeparatorChar);
|
||||||
|
if (pathParts.Length > 3)
|
||||||
|
{
|
||||||
|
newPath = Path.Combine(pathParts[pathParts.Length - 3], pathParts[pathParts.Length - 2], pathParts[pathParts.Length - 1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newPath = existingPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// strip the library root path from the existing path
|
||||||
|
if (existingPath.StartsWith(libraryRootPath))
|
||||||
|
{
|
||||||
|
newPath = existingPath.Substring(libraryRootPath.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newPath = existingPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Updating ROM path from " + existingPath + " to " + newPath);
|
||||||
|
|
||||||
|
sql = "UPDATE Games_Roms SET RelativePath = @newpath WHERE Id = @id;";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "newpath", newPath },
|
||||||
|
{ "id", romRow["Id"] }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all tables that have the prefix "Relation_" and drop them
|
||||||
|
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = @dbname AND table_name LIKE 'Relation_%';";
|
||||||
|
dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "dbname", Config.DatabaseConfiguration.DatabaseName }
|
||||||
|
};
|
||||||
|
data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
sql = "DROP TABLE " + (string)row["table_name"] + ";";
|
||||||
|
db.ExecuteNonQuery(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrating metadata is a safe background task
|
||||||
|
BackgroundUpgradeTargetSchemaVersions.Add(1024);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -60,11 +276,16 @@ namespace gaseous_server.Classes
|
|||||||
case 1002:
|
case 1002:
|
||||||
MySql_1002_MigrateMetadataVersion();
|
MySql_1002_MigrateMetadataVersion();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 1024:
|
||||||
|
MySql_1024_MigrateMetadataVersion();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MySql_1002_MigrateMetadataVersion() {
|
public static void MySql_1002_MigrateMetadataVersion()
|
||||||
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "";
|
string sql = "";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
@@ -159,5 +380,38 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MySql_1024_MigrateMetadataVersion()
|
||||||
|
{
|
||||||
|
FileSignature fileSignature = new FileSignature();
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT * FROM view_Games_Roms WHERE RomDataVersion = 1;";
|
||||||
|
DataTable data = db.ExecuteCMD(sql);
|
||||||
|
long count = 1;
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Database Migration", "Updating ROM table for ROM (" + count + " / " + data.Rows.Count + "): " + (string)row["Name"]);
|
||||||
|
|
||||||
|
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
|
||||||
|
Common.hashObject hash = new Common.hashObject()
|
||||||
|
{
|
||||||
|
md5hash = (string)row["MD5"],
|
||||||
|
sha1hash = (string)row["SHA1"]
|
||||||
|
};
|
||||||
|
Signatures_Games signature = fileSignature.GetFileSignature(
|
||||||
|
library,
|
||||||
|
hash,
|
||||||
|
new FileInfo((string)row["Path"]),
|
||||||
|
(string)row["Path"]
|
||||||
|
);
|
||||||
|
|
||||||
|
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
|
||||||
|
|
||||||
|
ImportGame.StoreGame(library, hash, signature, platform, (string)row["Path"], (long)row["Id"]);
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
572
gaseous-server/Classes/FileSignature.cs
Normal file
572
gaseous-server/Classes/FileSignature.cs
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Net;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
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;
|
||||||
|
bool signatureSelectorAlreadyApplied = false;
|
||||||
|
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
bool signatureSelector = false;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (signatureSelectorAlreadyApplied == false)
|
||||||
|
{
|
||||||
|
signatureSelector = true;
|
||||||
|
signatureSelectorAlreadyApplied = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveData archiveData = new ArchiveData
|
||||||
|
{
|
||||||
|
FileName = Path.GetFileName(file),
|
||||||
|
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
|
||||||
|
Size = zfi.Length,
|
||||||
|
MD5 = zhash.md5hash,
|
||||||
|
SHA1 = zhash.sha1hash,
|
||||||
|
isSignatureSelector = signatureSelector
|
||||||
|
};
|
||||||
|
archiveFiles.Add(archiveData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discoveredSignature.Rom.Attributes == null)
|
||||||
|
{
|
||||||
|
discoveredSignature.Rom.Attributes = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
discoveredSignature.Rom.Attributes.Add(
|
||||||
|
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get discovered platform
|
||||||
|
Platform? determinedPlatform = null;
|
||||||
|
if (library.DefaultPlatformId == null || library.DefaultPlatformId == 0)
|
||||||
|
{
|
||||||
|
determinedPlatform = Metadata.Platforms.GetPlatform((long)discoveredSignature.Flags.PlatformId);
|
||||||
|
if (determinedPlatform == null)
|
||||||
|
{
|
||||||
|
determinedPlatform = new Platform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
determinedPlatform = Metadata.Platforms.GetPlatform((long)library.DefaultPlatformId);
|
||||||
|
discoveredSignature.MetadataSources.AddPlatform((long)determinedPlatform.Id, determinedPlatform.Name, HasheousClient.Models.MetadataSources.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
return discoveredSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = null;
|
||||||
|
|
||||||
|
// begin signature search
|
||||||
|
switch (Config.MetadataConfiguration.SignatureSource)
|
||||||
|
{
|
||||||
|
case HasheousClient.Models.MetadataModel.SignatureSources.LocalOnly:
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Hasheous disabled - searching local database only");
|
||||||
|
|
||||||
|
discoveredSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HasheousClient.Models.MetadataModel.SignatureSources.Hasheous:
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Hasheous enabled - searching Hashesous and then local database if not found");
|
||||||
|
|
||||||
|
discoveredSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||||
|
|
||||||
|
if (discoveredSignature == null)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Signature not found in Hasheous - checking local database");
|
||||||
|
|
||||||
|
discoveredSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + discoveredSignature.Game.Name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discoveredSignature == null)
|
||||||
|
{
|
||||||
|
// construct a signature from file data
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Signature not found in local database or Hasheous (if enabled) - generating from file data");
|
||||||
|
|
||||||
|
discoveredSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + discoveredSignature.Game.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.PlatformName + " (" + discoveredSignature.Flags.PlatformId + ")");
|
||||||
|
|
||||||
|
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();
|
||||||
|
Console.WriteLine(HasheousClient.WebApp.HttpHelper.BaseUri);
|
||||||
|
HasheousClient.Models.LookupItemModel? HasheousResult = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// check the cache first
|
||||||
|
if (!Directory.Exists(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous()))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous());
|
||||||
|
}
|
||||||
|
// create file name from hash object
|
||||||
|
string cacheFileName = hash.md5hash + "_" + hash.sha1hash + ".json";
|
||||||
|
string cacheFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Hasheous(), cacheFileName);
|
||||||
|
// use cache file if it exists and is less than 30 days old, otherwise fetch from hasheous. if the fetch from hasheous is successful, save it to the cache, if it fails, use the cache if it exists even if it's old
|
||||||
|
if (File.Exists(cacheFilePath))
|
||||||
|
{
|
||||||
|
FileInfo cacheFile = new FileInfo(cacheFilePath);
|
||||||
|
if (cacheFile.LastWriteTimeUtc > DateTime.UtcNow.AddDays(-30))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Get Signature", "Using cached signature from Hasheous");
|
||||||
|
HasheousResult = Newtonsoft.Json.JsonConvert.DeserializeObject<HasheousClient.Models.LookupItemModel>(File.ReadAllText(cacheFilePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (HasheousResult == null)
|
||||||
|
{
|
||||||
|
// fetch from hasheous
|
||||||
|
HasheousResult = hasheous.RetrieveFromHasheous(new HasheousClient.Models.HashLookupModel
|
||||||
|
{
|
||||||
|
MD5 = hash.md5hash,
|
||||||
|
SHA1 = hash.sha1hash
|
||||||
|
});
|
||||||
|
|
||||||
|
if (HasheousResult != null)
|
||||||
|
{
|
||||||
|
// save to cache
|
||||||
|
File.WriteAllText(cacheFilePath, Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (File.Exists(cacheFilePath))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous - using cached signature", ex);
|
||||||
|
HasheousResult = Newtonsoft.Json.JsonConvert.DeserializeObject<HasheousClient.Models.LookupItemModel>(File.ReadAllText(cacheFilePath));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasheousResult != null)
|
||||||
|
{
|
||||||
|
if (HasheousResult.Signature != null)
|
||||||
|
{
|
||||||
|
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
|
||||||
|
string gameJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Game);
|
||||||
|
string romJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Rom);
|
||||||
|
signature.Game = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.GameItem>(gameJson);
|
||||||
|
signature.Rom = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.RomItem>(romJson);
|
||||||
|
|
||||||
|
// get platform metadata
|
||||||
|
if (HasheousResult.Platform != null)
|
||||||
|
{
|
||||||
|
if (HasheousResult.Platform.metadata.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Platform.metadata)
|
||||||
|
{
|
||||||
|
// only IGDB metadata is supported
|
||||||
|
if (metadataResult.Source == HasheousClient.Models.MetadataSources.IGDB)
|
||||||
|
{
|
||||||
|
if (metadataResult.ImmutableId.Length > 0)
|
||||||
|
{
|
||||||
|
// use immutable id
|
||||||
|
Platform hasheousPlatform = Platforms.GetPlatform(long.Parse(metadataResult.ImmutableId));
|
||||||
|
signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source);
|
||||||
|
}
|
||||||
|
else if (metadataResult.Id.Length > 0)
|
||||||
|
{
|
||||||
|
// fall back to id
|
||||||
|
Platform hasheousPlatform = Platforms.GetPlatform(metadataResult.Id);
|
||||||
|
signature.MetadataSources.AddPlatform((long)hasheousPlatform.Id, hasheousPlatform.Name, metadataResult.Source);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no id or immutable id - use unknown platform
|
||||||
|
signature.MetadataSources.AddPlatform(0, "Unknown Platform", HasheousClient.Models.MetadataSources.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get game metadata
|
||||||
|
if (HasheousResult.Metadata != null)
|
||||||
|
{
|
||||||
|
if (HasheousResult.Metadata.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
|
||||||
|
{
|
||||||
|
if (metadataResult.ImmutableId.Length > 0)
|
||||||
|
{
|
||||||
|
signature.MetadataSources.AddGame(long.Parse(metadataResult.ImmutableId), HasheousResult.Name, metadataResult.Source);
|
||||||
|
}
|
||||||
|
else if (metadataResult.Id.Length > 0)
|
||||||
|
{
|
||||||
|
switch (metadataResult.Source)
|
||||||
|
{
|
||||||
|
case HasheousClient.Models.MetadataSources.IGDB:
|
||||||
|
gaseous_server.Models.Game hasheousGame = Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, metadataResult.Id);
|
||||||
|
signature.MetadataSources.AddGame((long)hasheousGame.Id, hasheousGame.Name, metadataResult.Source);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (long.TryParse(metadataResult.Id, out long id) == true)
|
||||||
|
{
|
||||||
|
signature.MetadataSources.AddGame(id, HasheousResult.Name, metadataResult.Source);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
signature.MetadataSources.AddGame(0, "Unknown Game", HasheousClient.Models.MetadataSources.None);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no id or immutable id - use unknown game
|
||||||
|
signature.MetadataSources.AddGame(0, "Unknown Game", HasheousClient.Models.MetadataSources.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check attributes for a user manual link
|
||||||
|
if (HasheousResult.Attributes != null)
|
||||||
|
{
|
||||||
|
if (HasheousResult.Attributes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (HasheousClient.Models.AttributeItem attribute in HasheousResult.Attributes)
|
||||||
|
{
|
||||||
|
if (attribute.attributeName == HasheousClient.Models.AttributeItem.AttributeName.VIMMManualId)
|
||||||
|
{
|
||||||
|
signature.Game.UserManual = attribute.GetType().GetProperty("Link").GetValue(attribute).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (AggregateException aggEx)
|
||||||
|
{
|
||||||
|
foreach (Exception ex in aggEx.InnerExceptions)
|
||||||
|
{
|
||||||
|
// get exception type
|
||||||
|
if (ex is HttpRequestException)
|
||||||
|
{
|
||||||
|
if (ex.Message.Contains("404 (Not Found)"))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Get Signature", "No signature found in Hasheous");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
public bool isSignatureSelector { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,26 +7,26 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
public class Filters
|
public class Filters
|
||||||
{
|
{
|
||||||
public static Dictionary<string, object> Filter(Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
|
public static Dictionary<string, List<FilterItem>> Filter(Metadata.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
Dictionary<string, object> FilterSet = new Dictionary<string, object>();
|
Dictionary<string, List<FilterItem>> FilterSet = new Dictionary<string, List<FilterItem>>();
|
||||||
|
|
||||||
// platforms
|
// platforms
|
||||||
List<FilterItem> platforms = new List<FilterItem>();
|
List<FilterItem> platforms = new List<FilterItem>();
|
||||||
|
|
||||||
string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
string ageRestriction_Platform = "AgeGroup.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
||||||
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
||||||
if (IncludeUnrated == true)
|
if (IncludeUnrated == true)
|
||||||
{
|
{
|
||||||
ageRestriction_Platform += " OR Game.AgeGroupId IS NULL";
|
ageRestriction_Platform += " OR AgeGroup.AgeGroupId IS NULL";
|
||||||
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
|
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
|
||||||
}
|
}
|
||||||
|
|
||||||
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(view_Games.Id) AS GameCount FROM view_Games JOIN Relation_Game_Platforms ON Relation_Game_Platforms.GameId = view_Games.Id AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) JOIN Platform ON Platform.Id = Relation_Game_Platforms.PlatformsId WHERE (" + ageRestriction_Generic + ") GROUP BY Platform.`Name` ORDER BY Platform.`Name`;";
|
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, view_Games_Roms.PlatformId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , view_Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id AND Platform.SourceId = 0 GROUP BY Platform.`Name`;";
|
||||||
|
|
||||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// genres
|
// genres
|
||||||
List<FilterItem> genres = new List<FilterItem>();
|
List<FilterItem> genres = new List<FilterItem>();
|
||||||
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic);
|
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Platform);
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,7 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// game modes
|
// game modes
|
||||||
List<FilterItem> gameModes = new List<FilterItem>();
|
List<FilterItem> gameModes = new List<FilterItem>();
|
||||||
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic);
|
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Platform);
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// player perspectives
|
// player perspectives
|
||||||
List<FilterItem> playerPerspectives = new List<FilterItem>();
|
List<FilterItem> playerPerspectives = new List<FilterItem>();
|
||||||
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic);
|
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Platform);
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
@@ -71,7 +71,7 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
// themes
|
// themes
|
||||||
List<FilterItem> themes = new List<FilterItem>();
|
List<FilterItem> themes = new List<FilterItem>();
|
||||||
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic);
|
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Platform);
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
@@ -81,22 +81,24 @@ namespace gaseous_server.Classes
|
|||||||
FilterSet.Add("themes", themes);
|
FilterSet.Add("themes", themes);
|
||||||
|
|
||||||
// age groups
|
// age groups
|
||||||
List<FilterAgeGrouping> agegroupings = new List<FilterAgeGrouping>();
|
List<FilterItem> agegroupings = new List<FilterItem>();
|
||||||
sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;";
|
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
|
||||||
dbResponse = db.ExecuteCMD(sql);
|
dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||||
|
|
||||||
foreach (DataRow dr in dbResponse.Rows)
|
foreach (DataRow dr in dbResponse.Rows)
|
||||||
{
|
{
|
||||||
FilterAgeGrouping filterAgeGrouping = new FilterAgeGrouping();
|
FilterItem filterAgeGrouping = new FilterItem();
|
||||||
if (dr["AgeGroupId"] == DBNull.Value)
|
if (dr["AgeGroupId"] == DBNull.Value)
|
||||||
{
|
{
|
||||||
filterAgeGrouping.Id = (long)AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
|
filterAgeGrouping.Id = (int)(long)AgeGroups.AgeRestrictionGroupings.Unclassified;
|
||||||
filterAgeGrouping.AgeGroup = AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
|
filterAgeGrouping.Name = AgeGroups.AgeRestrictionGroupings.Unclassified.ToString();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filterAgeGrouping.Id = (long)(AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
|
int ageGroupLong = (int)dr["AgeGroupId"];
|
||||||
filterAgeGrouping.AgeGroup = (AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
|
AgeGroups.AgeRestrictionGroupings ageGroup = (AgeGroups.AgeRestrictionGroupings)ageGroupLong;
|
||||||
|
filterAgeGrouping.Id = ageGroupLong;
|
||||||
|
filterAgeGrouping.Name = ageGroup.ToString();
|
||||||
}
|
}
|
||||||
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
|
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
|
||||||
agegroupings.Add(filterAgeGrouping);
|
agegroupings.Add(filterAgeGrouping);
|
||||||
@@ -106,17 +108,24 @@ namespace gaseous_server.Classes
|
|||||||
return FilterSet;
|
return FilterSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic)
|
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction)
|
||||||
{
|
{
|
||||||
string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
|
//string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
|
||||||
|
|
||||||
|
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT view_Games_Roms.GameIdType, Game.Id, AgeGroup.AgeGroupId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId AND Game.GameIdType = Relation_Game_<ITEMNAME>s.GameSourceId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
|
||||||
sql = sql.Replace("<ITEMNAME>", Name);
|
sql = sql.Replace("<ITEMNAME>", Name);
|
||||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||||
|
|
||||||
return dbResponse;
|
return dbResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FilterItem
|
public class FilterItem
|
||||||
{
|
{
|
||||||
|
public FilterItem()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public FilterItem(DataRow dr)
|
public FilterItem(DataRow dr)
|
||||||
{
|
{
|
||||||
this.Id = (long)dr["Id"];
|
this.Id = (long)dr["Id"];
|
||||||
@@ -130,22 +139,5 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public int GameCount { get; set; }
|
public int GameCount { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FilterAgeGrouping
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
public AgeRatings.AgeGroups.AgeRestrictionGroupings AgeGroup { get ; set; }
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.AgeGroup.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GameCount { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using IGDB.Models;
|
|
||||||
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
|
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
|
||||||
|
|
||||||
namespace gaseous_server
|
namespace gaseous_server
|
||||||
@@ -13,25 +12,31 @@ namespace gaseous_server
|
|||||||
public class PathExists : Exception
|
public class PathExists : Exception
|
||||||
{
|
{
|
||||||
public PathExists(string path) : base("The library path " + path + " already exists.")
|
public PathExists(string path) : base("The library path " + path + " already exists.")
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PathNotFound : Exception
|
public class PathNotFound : Exception
|
||||||
{
|
{
|
||||||
public PathNotFound(string path) : base("The path " + path + " does not exist.")
|
public PathNotFound(string path) : base("The path " + path + " does not exist.")
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LibraryNotFound : Exception
|
public class LibraryNotFound : Exception
|
||||||
{
|
{
|
||||||
public LibraryNotFound(int LibraryId) : base("Library id " + LibraryId + " does not exist.")
|
public LibraryNotFound(int LibraryId) : base("Library id " + LibraryId + " does not exist.")
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CannotDeleteDefaultLibrary : Exception
|
public class CannotDeleteDefaultLibrary : Exception
|
||||||
{
|
{
|
||||||
public CannotDeleteDefaultLibrary() : base("Unable to delete the default library.")
|
public CannotDeleteDefaultLibrary() : base("Unable to delete the default library.")
|
||||||
{}
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CannotDeleteLibraryWhileScanIsActive : Exception
|
||||||
|
{
|
||||||
|
public CannotDeleteLibraryWhileScanIsActive() : base("Unable to delete library while a library scan is active. Wait for all scans to complete and try again")
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// code
|
// code
|
||||||
@@ -54,13 +59,25 @@ namespace gaseous_server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update default library path
|
||||||
|
public static void UpdateDefaultLibraryPath()
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "UPDATE GameLibraries SET Path=@path WHERE DefaultLibrary=1;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "path", Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Library") }
|
||||||
|
};
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<LibraryItem> GetLibraries
|
public static List<LibraryItem> GetLibraries
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
List<LibraryItem> libraryItems = new List<LibraryItem>();
|
List<LibraryItem> libraryItems = new List<LibraryItem>();
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM GameLibraries";
|
string sql = "SELECT * FROM GameLibraries ORDER BY `Name`;";
|
||||||
DataTable data = db.ExecuteCMD(sql);
|
DataTable data = db.ExecuteCMD(sql);
|
||||||
foreach (DataRow row in data.Rows)
|
foreach (DataRow row in data.Rows)
|
||||||
{
|
{
|
||||||
@@ -110,21 +127,42 @@ namespace gaseous_server
|
|||||||
|
|
||||||
int newLibraryId = (int)(long)data.Rows[0][0];
|
int newLibraryId = (int)(long)data.Rows[0][0];
|
||||||
|
|
||||||
return GetLibrary(newLibraryId);
|
Logging.Log(Logging.LogType.Information, "Library Management", "Created library " + Name + " at directory " + PathName);
|
||||||
|
|
||||||
|
LibraryItem library = GetLibrary(newLibraryId);
|
||||||
|
|
||||||
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DeleteLibrary(int LibraryId)
|
public static void DeleteLibrary(int LibraryId)
|
||||||
{
|
{
|
||||||
if (GetLibrary(LibraryId).IsDefaultLibrary == false)
|
LibraryItem library = GetLibrary(LibraryId);
|
||||||
|
if (library.IsDefaultLibrary == false)
|
||||||
{
|
{
|
||||||
|
// check for active library scans
|
||||||
|
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
(item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) ||
|
||||||
|
(item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker && item.ItemState == ProcessQueue.QueueItemState.Running)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete libraries while a library scan is running. Wait until the the library scan is completed and try again.");
|
||||||
|
throw new CannotDeleteLibraryWhileScanIsActive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
|
string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", LibraryId);
|
dbDict.Add("id", LibraryId);
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Library Management", "Deleted library " + library.Name + " at path " + library.Path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Library Management", "Unable to delete the default library.");
|
||||||
throw new CannotDeleteDefaultLibrary();
|
throw new CannotDeleteDefaultLibrary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,6 +186,24 @@ namespace gaseous_server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LibraryItem ScanLibrary(int LibraryId)
|
||||||
|
{
|
||||||
|
// add the library to scan to the queue
|
||||||
|
LibraryItem library = GetLibrary(LibraryId);
|
||||||
|
ImportGame.LibrariesToScan.Add(library);
|
||||||
|
|
||||||
|
// start the library scan if it's not already running
|
||||||
|
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||||
|
{
|
||||||
|
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState != ProcessQueue.QueueItemState.Running)
|
||||||
|
{
|
||||||
|
item.ForceExecute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
public class LibraryItem
|
public class LibraryItem
|
||||||
{
|
{
|
||||||
public LibraryItem(int Id, string Name, string Path, long DefaultPlatformId, bool IsDefaultLibrary)
|
public LibraryItem(int Id, string Name, string Path, long DefaultPlatformId, bool IsDefaultLibrary)
|
||||||
@@ -180,7 +236,7 @@ namespace gaseous_server
|
|||||||
{
|
{
|
||||||
if (_DefaultPlatformId != 0)
|
if (_DefaultPlatformId != 0)
|
||||||
{
|
{
|
||||||
Platform platform = Platforms.GetPlatform(_DefaultPlatformId);
|
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform(_DefaultPlatformId);
|
||||||
return platform.Name;
|
return platform.Name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ using System.Data;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Metadata.Ecma335;
|
using System.Reflection.Metadata.Ecma335;
|
||||||
|
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
public class Logging
|
public class Logging
|
||||||
@@ -21,8 +22,13 @@ namespace gaseous_server.Classes
|
|||||||
ExceptionValue = Common.ReturnValueIfNull(ExceptionValue, "").ToString()
|
ExceptionValue = Common.ReturnValueIfNull(ExceptionValue, "").ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_ = Task.Run(() => WriteLogAsync(logItem, LogToDiskOnly));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task WriteLogAsync(LogItem logItem, bool LogToDiskOnly)
|
||||||
|
{
|
||||||
bool AllowWrite = false;
|
bool AllowWrite = false;
|
||||||
if (EventType == LogType.Debug)
|
if (logItem.EventType == LogType.Debug)
|
||||||
{
|
{
|
||||||
if (Config.LoggingConfiguration.DebugLogging == true)
|
if (Config.LoggingConfiguration.DebugLogging == true)
|
||||||
{
|
{
|
||||||
@@ -42,26 +48,32 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
TraceOutput += Environment.NewLine + logItem.ExceptionValue.ToString();
|
TraceOutput += Environment.NewLine + logItem.ExceptionValue.ToString();
|
||||||
}
|
}
|
||||||
switch(logItem.EventType) {
|
string consoleColour = "";
|
||||||
|
switch (logItem.EventType)
|
||||||
|
{
|
||||||
case LogType.Information:
|
case LogType.Information:
|
||||||
Console.ForegroundColor = ConsoleColor.Blue;
|
// Console.ForegroundColor = ConsoleColor.Blue;
|
||||||
|
consoleColour = "\u001b[1;34m]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Warning:
|
case LogType.Warning:
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
// Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
consoleColour = "\u001b[1;33m]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Critical:
|
case LogType.Critical:
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
// Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
consoleColour = "\u001b[1;31m]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Debug:
|
case LogType.Debug:
|
||||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
// Console.ForegroundColor = ConsoleColor.Magenta;
|
||||||
|
consoleColour = "\u001b[1;36m]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
Console.WriteLine(TraceOutput);
|
Console.WriteLine(consoleColour + TraceOutput);
|
||||||
Console.ResetColor();
|
// Console.ResetColor();
|
||||||
|
|
||||||
if (WriteToDiskOnly == true)
|
if (WriteToDiskOnly == true)
|
||||||
{
|
{
|
||||||
@@ -109,10 +121,26 @@ namespace gaseous_server.Classes
|
|||||||
callingProcess = "";
|
callingProcess = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string callingUser;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CallContext.GetData("CallingUser").ToString() == null)
|
||||||
|
{
|
||||||
|
callingUser = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callingUser = CallContext.GetData("CallingUser").ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
callingUser = "";
|
||||||
|
}
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess);";
|
string sql = "INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception, CorrelationId, CallingProcess, CallingUser) VALUES (@EventTime, @EventType, @Process, @Message, @Exception, @correlationid, @callingprocess, @callinguser);";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
|
|
||||||
dbDict.Add("EventTime", logItem.EventTime);
|
dbDict.Add("EventTime", logItem.EventTime);
|
||||||
dbDict.Add("EventType", logItem.EventType);
|
dbDict.Add("EventType", logItem.EventType);
|
||||||
dbDict.Add("Process", logItem.Process);
|
dbDict.Add("Process", logItem.Process);
|
||||||
@@ -120,6 +148,7 @@ namespace gaseous_server.Classes
|
|||||||
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
|
dbDict.Add("Exception", Common.ReturnValueIfNull(logItem.ExceptionValue, "").ToString());
|
||||||
dbDict.Add("correlationid", correlationId);
|
dbDict.Add("correlationid", correlationId);
|
||||||
dbDict.Add("callingprocess", callingProcess);
|
dbDict.Add("callingprocess", callingProcess);
|
||||||
|
dbDict.Add("callinguser", callingUser);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -238,6 +267,15 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model.CallingUser != null)
|
||||||
|
{
|
||||||
|
if (model.CallingUser.Length > 0)
|
||||||
|
{
|
||||||
|
dbDict.Add("callingUser", model.CallingUser);
|
||||||
|
whereClauses.Add("CallingUser = @callingUser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// compile WHERE clause
|
// compile WHERE clause
|
||||||
string whereClause = "";
|
string whereClause = "";
|
||||||
if (whereClauses.Count > 0)
|
if (whereClauses.Count > 0)
|
||||||
@@ -252,7 +290,8 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
whereClause = "WHERE " + whereClause;
|
whereClause = "WHERE " + whereClause;
|
||||||
}
|
}
|
||||||
sql = "SELECT * FROM ServerLogs " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
|
|
||||||
|
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -260,7 +299,8 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
whereClause = "AND " + whereClause;
|
whereClause = "AND " + whereClause;
|
||||||
}
|
}
|
||||||
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
|
|
||||||
|
sql = "SELECT ServerLogs.Id, ServerLogs.EventTime, ServerLogs.EventType, ServerLogs.`Process`, ServerLogs.Message, ServerLogs.Exception, ServerLogs.CorrelationId, ServerLogs.CallingProcess, Users.Email FROM ServerLogs LEFT JOIN Users ON ServerLogs.CallingUser = Users.Id WHERE ServerLogs.Id < @StartIndex " + whereClause + " ORDER BY ServerLogs.Id DESC LIMIT @PageSize OFFSET @PageNumber;";
|
||||||
}
|
}
|
||||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
@@ -276,7 +316,8 @@ namespace gaseous_server.Classes
|
|||||||
Message = (string)row["Message"],
|
Message = (string)row["Message"],
|
||||||
ExceptionValue = (string)row["Exception"],
|
ExceptionValue = (string)row["Exception"],
|
||||||
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
|
CorrelationId = (string)Common.ReturnValueIfNull(row["CorrelationId"], ""),
|
||||||
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], "")
|
CallingProcess = (string)Common.ReturnValueIfNull(row["CallingProcess"], ""),
|
||||||
|
CallingUser = (string)Common.ReturnValueIfNull(row["Email"], "")
|
||||||
};
|
};
|
||||||
|
|
||||||
logs.Add(log);
|
logs.Add(log);
|
||||||
@@ -301,6 +342,7 @@ namespace gaseous_server.Classes
|
|||||||
public string Process { get; set; } = "";
|
public string Process { get; set; } = "";
|
||||||
public string CorrelationId { get; set; } = "";
|
public string CorrelationId { get; set; } = "";
|
||||||
public string? CallingProcess { get; set; } = "";
|
public string? CallingProcess { get; set; } = "";
|
||||||
|
public string? CallingUser { get; set; } = "";
|
||||||
private string _Message = "";
|
private string _Message = "";
|
||||||
public string Message
|
public string Message
|
||||||
{
|
{
|
||||||
@@ -327,6 +369,7 @@ namespace gaseous_server.Classes
|
|||||||
public string? SearchText { get; set; }
|
public string? SearchText { get; set; }
|
||||||
public string? CorrelationId { get; set; }
|
public string? CorrelationId { get; set; }
|
||||||
public string? CallingProcess { get; set; }
|
public string? CallingProcess { get; set; }
|
||||||
|
public string? CallingUser { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,55 @@ namespace gaseous_server.Classes
|
|||||||
{
|
{
|
||||||
const int MaxFileAge = 30;
|
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
|
||||||
|
Logging.Log(Logging.LogType.Information, "Maintenance", "Removing 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
|
||||||
|
Logging.Log(Logging.LogType.Information, "Maintenance", "Removing logs older than " + Config.LoggingConfiguration.LogRetention + " days");
|
||||||
|
long deletedCount = 1;
|
||||||
|
long deletedEventCount = 0;
|
||||||
|
long maxLoops = 10000;
|
||||||
|
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRetentionDate LIMIT 1000; SELECT ROW_COUNT() AS Count;";
|
||||||
|
dbDict.Add("EventRetentionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
|
||||||
|
while (deletedCount > 0)
|
||||||
|
{
|
||||||
|
DataTable deletedCountTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
deletedCount = (long)deletedCountTable.Rows[0][0];
|
||||||
|
deletedEventCount += deletedCount;
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Maintenance", "Deleted " + deletedCount + " log entries");
|
||||||
|
|
||||||
|
// check if we've hit the limit
|
||||||
|
maxLoops -= 1;
|
||||||
|
if (maxLoops <= 0)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Maintenance", "Hit the maximum number of loops for deleting logs. Stopping.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logging.Log(Logging.LogType.Information, "Maintenance", "Deleted " + deletedEventCount + " log entries");
|
||||||
|
|
||||||
// delete files and directories older than 7 days in PathsToClean
|
// delete files and directories older than 7 days in PathsToClean
|
||||||
List<string> PathsToClean = new List<string>();
|
List<string> PathsToClean = new List<string>();
|
||||||
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
|
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
|
||||||
@@ -43,10 +90,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");
|
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables");
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
sql = "SHOW FULL TABLES WHERE Table_Type = 'BASE TABLE';";
|
||||||
string sql = "SHOW TABLES;";
|
|
||||||
DataTable tables = db.ExecuteCMD(sql);
|
DataTable tables = db.ExecuteCMD(sql);
|
||||||
|
|
||||||
int StatusCounter = 1;
|
int StatusCounter = 1;
|
||||||
@@ -55,7 +108,7 @@ namespace gaseous_server.Classes
|
|||||||
SetStatus(StatusCounter, tables.Rows.Count, "Optimising table " + row[0].ToString());
|
SetStatus(StatusCounter, tables.Rows.Count, "Optimising table " + row[0].ToString());
|
||||||
|
|
||||||
sql = "OPTIMIZE TABLE " + row[0].ToString();
|
sql = "OPTIMIZE TABLE " + row[0].ToString();
|
||||||
DataTable response = db.ExecuteCMD(sql);
|
DataTable response = db.ExecuteCMD(sql, new Dictionary<string, object>(), 240);
|
||||||
foreach (DataRow responseRow in response.Rows)
|
foreach (DataRow responseRow in response.Rows)
|
||||||
{
|
{
|
||||||
string retVal = "";
|
string retVal = "";
|
||||||
|
|||||||
316
gaseous-server/Classes/Metadata/AgeGroups.cs
Normal file
316
gaseous-server/Classes/Metadata/AgeGroups.cs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
using Microsoft.CodeAnalysis.Classification;
|
||||||
|
|
||||||
|
namespace gaseous_server.Classes.Metadata
|
||||||
|
{
|
||||||
|
public class AgeGroups
|
||||||
|
{
|
||||||
|
public AgeGroups()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AgeGroup? GetAgeGroup(Models.Game? game)
|
||||||
|
{
|
||||||
|
if (game == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||||
|
cacheStatus = Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "AgeGroup", (long)game.Id);
|
||||||
|
|
||||||
|
AgeGroup? RetVal = new AgeGroup();
|
||||||
|
|
||||||
|
switch (cacheStatus)
|
||||||
|
{
|
||||||
|
case Storage.CacheStatus.NotPresent:
|
||||||
|
RetVal = _GetAgeGroup(game);
|
||||||
|
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, RetVal, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Storage.CacheStatus.Expired:
|
||||||
|
RetVal = _GetAgeGroup(game);
|
||||||
|
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, RetVal, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Storage.CacheStatus.Current:
|
||||||
|
RetVal = Storage.GetCacheValue<AgeGroup>(HasheousClient.Models.MetadataSources.IGDB, RetVal, "Id", game.Id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception("How did you get here?");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RetVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AgeGroup? _GetAgeGroup(Models.Game game)
|
||||||
|
{
|
||||||
|
// compile the maximum age group for the given game
|
||||||
|
if (game != null)
|
||||||
|
{
|
||||||
|
if (game.AgeRatings != null)
|
||||||
|
{
|
||||||
|
if (game.AgeRatings != null)
|
||||||
|
{
|
||||||
|
// collect ratings values from metadata
|
||||||
|
List<AgeRating> ageRatings = new List<AgeRating>();
|
||||||
|
foreach (long ratingId in game.AgeRatings)
|
||||||
|
{
|
||||||
|
AgeRating? rating = AgeRatings.GetAgeRating(game.MetadataSource, ratingId);
|
||||||
|
if (rating != null)
|
||||||
|
{
|
||||||
|
ageRatings.Add(rating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile the ratings values into the ratings groups
|
||||||
|
AgeRestrictionGroupings highestAgeGroup = GetAgeGroupFromAgeRatings(ageRatings);
|
||||||
|
|
||||||
|
// return the compiled ratings group
|
||||||
|
AgeGroup ageGroup = new AgeGroup();
|
||||||
|
ageGroup.Id = game.Id;
|
||||||
|
ageGroup.GameId = game.Id;
|
||||||
|
if (highestAgeGroup == 0)
|
||||||
|
{
|
||||||
|
ageGroup.AgeGroupId = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ageGroup.AgeGroupId = highestAgeGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ageGroup;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AgeGroup ageGroup = new AgeGroup();
|
||||||
|
ageGroup.Id = game.Id;
|
||||||
|
ageGroup.GameId = game.Id;
|
||||||
|
ageGroup.AgeGroupId = null;
|
||||||
|
|
||||||
|
return ageGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AgeGroup ageGroup = new AgeGroup();
|
||||||
|
ageGroup.Id = game.Id;
|
||||||
|
ageGroup.GameId = game.Id;
|
||||||
|
ageGroup.AgeGroupId = null;
|
||||||
|
|
||||||
|
return ageGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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; }
|
||||||
|
public long? GameId { get; set; }
|
||||||
|
public AgeRestrictionGroupings? AgeGroupId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Adult, Adult_Item
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Mature, Mature_Item
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Teen, Teen_Item
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgeRestrictionGroupings.Child, Child_Item
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AgeRestrictionGroupings
|
||||||
|
{
|
||||||
|
Adult = 4,
|
||||||
|
Mature = 3,
|
||||||
|
Teen = 2,
|
||||||
|
Child = 1,
|
||||||
|
Unclassified = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> RatingsBoards
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<string> boards = new List<string>{
|
||||||
|
"ACB", "CERO", "CLASS_IND", "ESRB", "GRAC", "PEGI", "USK"
|
||||||
|
};
|
||||||
|
|
||||||
|
return boards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly static AgeGroupItem Adult_Item = new AgeGroupItem
|
||||||
|
{
|
||||||
|
ACB = new List<AgeRatingTitle> { AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
|
||||||
|
CERO = new List<AgeRatingTitle> { AgeRatingTitle.CERO_Z },
|
||||||
|
CLASS_IND = new List<AgeRatingTitle> { AgeRatingTitle.CLASS_IND_Eighteen },
|
||||||
|
ESRB = new List<AgeRatingTitle> { AgeRatingTitle.RP, AgeRatingTitle.AO },
|
||||||
|
GRAC = new List<AgeRatingTitle> { AgeRatingTitle.GRAC_Eighteen },
|
||||||
|
PEGI = new List<AgeRatingTitle> { AgeRatingTitle.Eighteen },
|
||||||
|
USK = new List<AgeRatingTitle> { AgeRatingTitle.USK_18 }
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly static AgeGroupItem Mature_Item = new AgeGroupItem
|
||||||
|
{
|
||||||
|
ACB = new List<AgeRatingTitle> { AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
|
||||||
|
CERO = new List<AgeRatingTitle> { AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
|
||||||
|
CLASS_IND = new List<AgeRatingTitle> { AgeRatingTitle.CLASS_IND_Sixteen },
|
||||||
|
ESRB = new List<AgeRatingTitle> { AgeRatingTitle.M },
|
||||||
|
GRAC = new List<AgeRatingTitle> { AgeRatingTitle.GRAC_Fifteen },
|
||||||
|
PEGI = new List<AgeRatingTitle> { AgeRatingTitle.Sixteen },
|
||||||
|
USK = new List<AgeRatingTitle> { AgeRatingTitle.USK_16 }
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly static AgeGroupItem Teen_Item = new AgeGroupItem
|
||||||
|
{
|
||||||
|
ACB = new List<AgeRatingTitle> { AgeRatingTitle.ACB_PG },
|
||||||
|
CERO = new List<AgeRatingTitle> { AgeRatingTitle.CERO_B },
|
||||||
|
CLASS_IND = new List<AgeRatingTitle> { AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
|
||||||
|
ESRB = new List<AgeRatingTitle> { AgeRatingTitle.T },
|
||||||
|
GRAC = new List<AgeRatingTitle> { AgeRatingTitle.GRAC_Twelve },
|
||||||
|
PEGI = new List<AgeRatingTitle> { AgeRatingTitle.Twelve },
|
||||||
|
USK = new List<AgeRatingTitle> { AgeRatingTitle.USK_12 }
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly static AgeGroupItem Child_Item = new AgeGroupItem
|
||||||
|
{
|
||||||
|
ACB = new List<AgeRatingTitle> { AgeRatingTitle.ACB_G },
|
||||||
|
CERO = new List<AgeRatingTitle> { AgeRatingTitle.CERO_A },
|
||||||
|
CLASS_IND = new List<AgeRatingTitle> { AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
|
||||||
|
ESRB = new List<AgeRatingTitle> { AgeRatingTitle.EC, AgeRatingTitle.E, AgeRatingTitle.E10 },
|
||||||
|
GRAC = new List<AgeRatingTitle> { AgeRatingTitle.GRAC_All },
|
||||||
|
PEGI = new List<AgeRatingTitle> { AgeRatingTitle.Three, AgeRatingTitle.Seven },
|
||||||
|
USK = new List<AgeRatingTitle> { AgeRatingTitle.USK_0, AgeRatingTitle.USK_6 }
|
||||||
|
};
|
||||||
|
|
||||||
|
public class AgeGroupItem
|
||||||
|
{
|
||||||
|
public List<AgeRatingTitle> ACB { get; set; }
|
||||||
|
public List<AgeRatingTitle> CERO { get; set; }
|
||||||
|
public List<AgeRatingTitle> CLASS_IND { get; set; }
|
||||||
|
public List<AgeRatingTitle> ESRB { get; set; }
|
||||||
|
public List<AgeRatingTitle> GRAC { get; set; }
|
||||||
|
public List<AgeRatingTitle> PEGI { get; set; }
|
||||||
|
public List<AgeRatingTitle> USK { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
|
public List<long> AgeGroupItemValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<long> values = new List<long>();
|
||||||
|
{
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in ACB)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in CERO)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in ESRB)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in GRAC)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in PEGI)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
foreach (AgeRatingTitle ageRatingTitle in USK)
|
||||||
|
{
|
||||||
|
values.Add((long)ageRatingTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
using Microsoft.CodeAnalysis.Classification;
|
using Microsoft.CodeAnalysis.Classification;
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class AgeRatings
|
public class AgeRatings
|
||||||
{
|
{
|
||||||
const string fieldList = "fields category,checksum,content_descriptions,rating,rating_cover_url,synopsis;";
|
public const string fieldList = "fields category,checksum,content_descriptions,rating,rating_cover_url,synopsis;";
|
||||||
|
|
||||||
public AgeRatings()
|
public AgeRatings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AgeRating? GetAgeRatings(long? Id)
|
public static AgeRating? GetAgeRating(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -23,106 +23,16 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<AgeRating> RetVal = _GetAgeRatings(SearchUsing.id, Id);
|
AgeRating? RetVal = Metadata.GetMetadata<AgeRating>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AgeRating GetAgeRatings(string Slug)
|
public static GameAgeRating GetConsolidatedAgeRating(HasheousClient.Models.MetadataSources SourceType, long RatingId)
|
||||||
{
|
|
||||||
Task<AgeRating> RetVal = _GetAgeRatings(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AgeRating> _GetAgeRatings(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AgeRating", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AgeRating", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
AgeRating returnValue = new AgeRating();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
UpdateSubClasses(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateSubClasses(AgeRating ageRating)
|
|
||||||
{
|
|
||||||
if (ageRating.ContentDescriptions != null)
|
|
||||||
{
|
|
||||||
foreach (long AgeRatingContentDescriptionId in ageRating.ContentDescriptions.Ids)
|
|
||||||
{
|
|
||||||
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(AgeRatingContentDescriptionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AgeRating> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get AgeRatings metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<AgeRating>(IGDBClient.Endpoints.AgeRating, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameAgeRating GetConsolidatedAgeRating(long RatingId)
|
|
||||||
{
|
{
|
||||||
GameAgeRating gameAgeRating = new GameAgeRating();
|
GameAgeRating gameAgeRating = new GameAgeRating();
|
||||||
|
|
||||||
AgeRating ageRating = GetAgeRatings(RatingId);
|
AgeRating ageRating = GetAgeRating(SourceType, RatingId);
|
||||||
gameAgeRating.Id = (long)ageRating.Id;
|
gameAgeRating.Id = (long)ageRating.Id;
|
||||||
gameAgeRating.RatingBoard = (AgeRatingCategory)ageRating.Category;
|
gameAgeRating.RatingBoard = (AgeRatingCategory)ageRating.Category;
|
||||||
gameAgeRating.RatingTitle = (AgeRatingTitle)ageRating.Rating;
|
gameAgeRating.RatingTitle = (AgeRatingTitle)ageRating.Rating;
|
||||||
@@ -130,9 +40,9 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
List<string> descriptions = new List<string>();
|
List<string> descriptions = new List<string>();
|
||||||
if (ageRating.ContentDescriptions != null)
|
if (ageRating.ContentDescriptions != null)
|
||||||
{
|
{
|
||||||
foreach (long ContentId in ageRating.ContentDescriptions.Ids)
|
foreach (long ContentId in ageRating.ContentDescriptions)
|
||||||
{
|
{
|
||||||
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(ContentId);
|
AgeRatingContentDescription ageRatingContentDescription = AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(SourceType, ContentId);
|
||||||
descriptions.Add(ageRatingContentDescription.Description);
|
descriptions.Add(ageRatingContentDescription.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +67,7 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
db.ExecuteNonQuery(sql);
|
db.ExecuteNonQuery(sql);
|
||||||
|
|
||||||
// loop all age groups
|
// loop all age groups
|
||||||
foreach(KeyValuePair<AgeGroups.AgeRestrictionGroupings, AgeGroups.AgeGroupItem> ageGrouping in AgeGroups.AgeGroupingsFlat)
|
foreach (KeyValuePair<AgeGroups.AgeRestrictionGroupings, AgeGroups.AgeGroupItem> ageGrouping in AgeGroups.AgeGroupingsFlat)
|
||||||
{
|
{
|
||||||
AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value;
|
AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value;
|
||||||
var properties = ageGroupItem.GetType().GetProperties();
|
var properties = ageGroupItem.GetType().GetProperties();
|
||||||
@@ -186,158 +96,6 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AgeGroups
|
|
||||||
{
|
|
||||||
public AgeGroups()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Adult, Adult_Item
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Mature, Mature_Item
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Teen, Teen_Item
|
|
||||||
},
|
|
||||||
{
|
|
||||||
AgeRestrictionGroupings.Child, Child_Item
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum AgeRestrictionGroupings
|
|
||||||
{
|
|
||||||
Adult = 4,
|
|
||||||
Mature = 3,
|
|
||||||
Teen = 2,
|
|
||||||
Child = 1,
|
|
||||||
Unclassified = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
|
|
||||||
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
|
|
||||||
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
|
|
||||||
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
|
|
||||||
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
|
|
||||||
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
|
|
||||||
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
|
|
||||||
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
|
|
||||||
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
|
|
||||||
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
|
|
||||||
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
|
|
||||||
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
|
|
||||||
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
|
|
||||||
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
|
|
||||||
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
|
|
||||||
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
|
|
||||||
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
|
|
||||||
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
|
|
||||||
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
|
|
||||||
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
|
|
||||||
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
|
|
||||||
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
|
|
||||||
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
|
|
||||||
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
|
|
||||||
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
|
|
||||||
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
|
|
||||||
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
|
|
||||||
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
|
|
||||||
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
|
|
||||||
};
|
|
||||||
|
|
||||||
public class AgeGroupItem
|
|
||||||
{
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
|
|
||||||
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
|
||||||
public List<long> AgeGroupItemValues
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
List<long> values = new List<long>();
|
|
||||||
{
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in ACB)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in CERO)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in ESRB)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in GRAC)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in PEGI)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
foreach (AgeRatingTitle ageRatingTitle in USK)
|
|
||||||
{
|
|
||||||
values.Add((long)ageRatingTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class AgeRatingContentDescriptions
|
public class AgeRatingContentDescriptions
|
||||||
{
|
{
|
||||||
const string fieldList = "fields category,checksum,description;";
|
public const string fieldList = "fields category,checksum,description;";
|
||||||
|
|
||||||
public AgeRatingContentDescriptions()
|
public AgeRatingContentDescriptions()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(long? Id)
|
public static AgeRatingContentDescription? GetAgeRatingContentDescriptions(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<AgeRatingContentDescription> RetVal = _GetAgeRatingContentDescriptions(SearchUsing.id, Id);
|
AgeRatingContentDescription? RetVal = Metadata.GetMetadata<AgeRatingContentDescription>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AgeRatingContentDescription GetAgeRatingContentDescriptions(string Slug)
|
|
||||||
{
|
|
||||||
Task<AgeRatingContentDescription> RetVal = _GetAgeRatingContentDescriptions(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AgeRatingContentDescription> _GetAgeRatingContentDescriptions(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AgeRatingContentDescription", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AgeRatingContentDescription", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
AgeRatingContentDescription returnValue = new AgeRatingContentDescription();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AgeRatingContentDescription> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get AgeRatingContentDescriptionContentDescriptions metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<AgeRatingContentDescription>(IGDBClient.Endpoints.AgeRatingContentDescriptions, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class AlternativeNames
|
public class AlternativeNames
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,comment,game,name;";
|
public const string fieldList = "fields checksum,comment,game,name;";
|
||||||
|
|
||||||
public AlternativeNames()
|
public AlternativeNames()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AlternativeName? GetAlternativeNames(long? Id)
|
public static AlternativeName? GetAlternativeNames(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<AlternativeName> RetVal = _GetAlternativeNames(SearchUsing.id, Id);
|
AlternativeName? RetVal = Metadata.GetMetadata<AlternativeName>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AlternativeName GetAlternativeNames(string Slug)
|
|
||||||
{
|
|
||||||
Task<AlternativeName> RetVal = _GetAlternativeNames(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AlternativeName> _GetAlternativeNames(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AlternativeName", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("AlternativeName", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
AlternativeName returnValue = new AlternativeName();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<AlternativeName> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get AlternativeNames metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<AlternativeName>(IGDBClient.Endpoints.AlternativeNames, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Artworks
|
public class Artworks
|
||||||
{
|
{
|
||||||
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
public const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
||||||
|
|
||||||
public Artworks()
|
public Artworks()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Artwork? GetArtwork(long? Id, string LogoPath)
|
public static Artwork? GetArtwork(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,148 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.id, Id, LogoPath);
|
Artwork? RetVal = Metadata.GetMetadata<Artwork>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Artwork GetArtwork(string Slug, string LogoPath)
|
|
||||||
{
|
|
||||||
Task<Artwork> RetVal = _GetArtwork(SearchUsing.slug, Slug, LogoPath);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Artwork> _GetArtwork(SearchUsing searchUsing, object searchValue, string LogoPath)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Artwork", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Artwork", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Artwork returnValue = new Artwork();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
LogoPath = Path.Combine(LogoPath, "Artwork");
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!File.Exists(Path.Combine(LogoPath, returnValue.ImageId + ".jpg"))) || forceImageDownload == 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Artwork> GetObjectFromServer(string WhereClause, string LogoPath)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Collections
|
public class Collections
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
|
public const string fieldList = "fields as_child_relations,as_parent_relations,checksum,created_at,games,name,slug,type,updated_at,url;";
|
||||||
|
|
||||||
public Collections()
|
public Collections()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection? GetCollections(long? Id)
|
public static Collection? GetCollections(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Collection> RetVal = _GetCollections(SearchUsing.id, Id);
|
Collection? RetVal = Metadata.GetMetadata<Collection>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection GetCollections(string Slug)
|
|
||||||
{
|
|
||||||
Task<Collection> RetVal = _GetCollections(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Collection> _GetCollections(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Collection", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Collection", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection returnValue = new Collection();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Collection> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Collections metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Collection>(IGDBClient.Endpoints.Collections, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Companies
|
public class Companies
|
||||||
{
|
{
|
||||||
const string fieldList = "fields change_date,change_date_category,changed_company_id,checksum,country,created_at,description,developed,logo,name,parent,published,slug,start_date,start_date_category,updated_at,url,websites;";
|
public const string fieldList = "fields change_date,change_date_category,changed_company_id,checksum,country,created_at,description,developed,logo,name,parent,published,slug,start_date,start_date_category,updated_at,url,websites;";
|
||||||
|
|
||||||
public Companies()
|
public Companies()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Company? GetCompanies(long? Id)
|
public static Company? GetCompanies(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -20,105 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Company> RetVal = _GetCompanies(SearchUsing.id, Id);
|
Company? RetVal = Metadata.GetMetadata<Company>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Company GetCompanies(string Slug)
|
|
||||||
{
|
|
||||||
Task<Company> RetVal = _GetCompanies(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Company> _GetCompanies(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Company", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Company", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Company returnValue = new Company();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
if (returnValue != null) { Storage.NewCacheValue(returnValue); }
|
|
||||||
UpdateSubClasses(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateSubClasses(Company company)
|
|
||||||
{
|
|
||||||
if (company.Logo != null)
|
|
||||||
{
|
|
||||||
CompanyLogo companyLogo = CompanyLogos.GetCompanyLogo(company.Logo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Company(company));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Company> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Companies metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Company>(IGDBClient.Endpoints.Companies, fieldList, WhereClause);
|
|
||||||
if (results.Length > 0)
|
|
||||||
{
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class CompanyLogos
|
public class CompanyLogos
|
||||||
{
|
{
|
||||||
const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
|
public const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
|
||||||
|
|
||||||
public CompanyLogos()
|
public CompanyLogos()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompanyLogo? GetCompanyLogo(long? Id, string LogoPath)
|
public static CompanyLogo? GetCompanyLogo(long? Id, string ImagePath)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,153 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.id, Id, LogoPath);
|
CompanyLogo? RetVal = Metadata.GetMetadata<CompanyLogo>((long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompanyLogo GetCompanyLogo(string Slug, string LogoPath)
|
|
||||||
{
|
|
||||||
Task<CompanyLogo> RetVal = _GetCompanyLogo(SearchUsing.slug, Slug, LogoPath);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<CompanyLogo> _GetCompanyLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("CompanyLogo", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("CompanyLogo", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
CompanyLogo returnValue = new CompanyLogo();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<CompanyLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
|
|
||||||
{
|
|
||||||
// get CompanyLogo 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,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Covers
|
public class Covers
|
||||||
{
|
{
|
||||||
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
public const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
|
||||||
|
|
||||||
public Covers()
|
public Covers()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Cover? GetCover(long? Id, string LogoPath)
|
public static Cover? GetCover(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,147 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Cover> RetVal = _GetCover(SearchUsing.id, Id, LogoPath);
|
Cover? RetVal = Metadata.GetMetadata<Cover>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Cover GetCover(string Slug, string LogoPath)
|
|
||||||
{
|
|
||||||
Task<Cover> RetVal = _GetCover(SearchUsing.slug, Slug, LogoPath);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Cover> _GetCover(SearchUsing searchUsing, object searchValue, string LogoPath)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Cover", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Cover", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cover returnValue = new Cover();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!File.Exists(Path.Combine(LogoPath, "Cover.jpg"))) || forceImageDownload == 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Cover> GetObjectFromServer(string WhereClause, string LogoPath)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class ExternalGames
|
public class ExternalGames
|
||||||
{
|
{
|
||||||
const string fieldList = "fields category,checksum,countries,created_at,game,media,name,platform,uid,updated_at,url,year;";
|
public const string fieldList = "fields category,checksum,countries,created_at,game,media,name,platform,uid,updated_at,url,year;";
|
||||||
|
|
||||||
public ExternalGames()
|
public ExternalGames()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExternalGame? GetExternalGames(long? Id)
|
public static ExternalGame? GetExternalGames(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,96 +19,8 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<ExternalGame> RetVal = _GetExternalGames(SearchUsing.id, Id);
|
ExternalGame? RetVal = Metadata.GetMetadata<ExternalGame>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ExternalGame GetExternalGames(string Slug)
|
|
||||||
{
|
|
||||||
Task<ExternalGame> RetVal = _GetExternalGames(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<ExternalGame> _GetExternalGames(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("ExternalGame", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("ExternalGame", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalGame returnValue = new ExternalGame();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<ExternalGame?> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get ExternalGames metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<ExternalGame>(IGDBClient.Endpoints.ExternalGames, fieldList, WhereClause);
|
|
||||||
if (results.Length > 0)
|
|
||||||
{
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Franchises
|
public class Franchises
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
|
public const string fieldList = "fields checksum,created_at,games,name,slug,updated_at,url;";
|
||||||
|
|
||||||
public Franchises()
|
public Franchises()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Franchise? GetFranchises(long? Id)
|
public static Franchise? GetFranchises(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +19,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Franchise> RetVal = _GetFranchises(SearchUsing.id, Id);
|
Franchise? RetVal = Metadata.GetMetadata<Franchise>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Franchise GetFranchises(string Slug)
|
|
||||||
{
|
|
||||||
Task<Franchise> RetVal = _GetFranchises(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Franchise> _GetFranchises(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Franchise", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Franchise", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Franchise returnValue = new Franchise();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Franchise> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get FranchiseContentDescriptions metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Franchise>(IGDBClient.Endpoints.Franchies, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class GameModes
|
public class GameModes
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
||||||
|
|
||||||
public GameModes()
|
public GameModes()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameMode? GetGame_Modes(long? Id)
|
public static GameMode? GetGame_Modes(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<GameMode> RetVal = _GetGame_Modes(SearchUsing.id, Id);
|
GameMode? RetVal = Metadata.GetMetadata<GameMode>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameMode GetGame_Modes(string Slug)
|
|
||||||
{
|
|
||||||
Task<GameMode> RetVal = _GetGame_Modes(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameMode> _GetGame_Modes(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("GameMode", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("GameMode", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
GameMode returnValue = new GameMode();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameMode> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game_Modes metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<GameMode>(IGDBClient.Endpoints.GameModes, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class GamesVideos
|
public class GamesVideos
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,game,name,video_id;";
|
public const string fieldList = "fields checksum,game,name,video_id;";
|
||||||
|
|
||||||
public GamesVideos()
|
public GamesVideos()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameVideo? GetGame_Videos(long? Id)
|
public static GameVideo? GetGame_Videos(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<GameVideo> RetVal = _GetGame_Videos(SearchUsing.id, Id);
|
GameVideo? RetVal = Metadata.GetMetadata<GameVideo>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameVideo GetGame_Videos(string Slug)
|
|
||||||
{
|
|
||||||
Task<GameVideo> RetVal = _GetGame_Videos(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameVideo> _GetGame_Videos(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("GameVideo", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("GameVideo", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
GameVideo returnValue = new GameVideo();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameVideo> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game_Videos metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<GameVideo>(IGDBClient.Endpoints.GameVideos, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using IGDB;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using IGDB.Models;
|
using gaseous_server.Models;
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Games
|
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;";
|
public const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collections,cover,created_at,dlcs,expanded_games,expansions,external_games,first_release_date,follows,forks,franchise,franchises,game_engines,game_localizations,game_modes,genres,hypes,involved_companies,keywords,language_supports,multiplayer_modes,name,parent_game,platforms,player_perspectives,ports,rating,rating_count,release_dates,remakes,remasters,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;";
|
||||||
|
|
||||||
public Games()
|
public Games()
|
||||||
{
|
{
|
||||||
@@ -17,42 +17,35 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
public class InvalidGameId : Exception
|
public class InvalidGameId : Exception
|
||||||
{
|
{
|
||||||
public InvalidGameId(long Id) : base("Unable to find Game by id " + Id)
|
public InvalidGameId(long Id) : base("Unable to find Game by id " + Id)
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
public static Game? GetGame(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if (Id == 0)
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
Game returnValue = new Game();
|
return null;
|
||||||
if (Storage.GetCacheStatus("Game", 0) == Storage.CacheStatus.NotPresent)
|
|
||||||
{
|
|
||||||
returnValue = new Game
|
|
||||||
{
|
|
||||||
Id = 0,
|
|
||||||
Name = "Unknown Title",
|
|
||||||
Slug = "Unknown"
|
|
||||||
};
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Storage.GetCacheValue<Game>(returnValue, "id", 0);
|
Game? RetVal = Metadata.GetMetadata<Game>(SourceType, (long)Id, false);
|
||||||
}
|
RetVal.MetadataSource = SourceType;
|
||||||
}
|
long? metadataMap = MetadataManagement.GetMetadataMapIdFromSourceId(SourceType, (long)Id);
|
||||||
else
|
if (metadataMap != null)
|
||||||
{
|
{
|
||||||
Task<Game> RetVal = _GetGame(SearchUsing.id, Id, getAllMetadata, followSubGames, forceRefresh);
|
RetVal.MetadataMapId = (long)metadataMap;
|
||||||
return RetVal.Result;
|
}
|
||||||
|
RetVal = MassageResult(RetVal);
|
||||||
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Game GetGame(string Slug, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
public static Game? GetGame(HasheousClient.Models.MetadataSources SourceType, string? Slug)
|
||||||
{
|
{
|
||||||
Task<Game> RetVal = _GetGame(SearchUsing.slug, Slug, getAllMetadata, followSubGames, forceRefresh);
|
Game? RetVal = Metadata.GetMetadata<Game>(SourceType, Slug, false);
|
||||||
return RetVal.Result;
|
RetVal.MetadataSource = SourceType;
|
||||||
|
RetVal = MassageResult(RetVal);
|
||||||
|
return RetVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Game GetGame(DataRow dataRow)
|
public static Game GetGame(DataRow dataRow)
|
||||||
@@ -60,305 +53,378 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
return Storage.BuildCacheObject<Game>(new Game(), dataRow);
|
return Storage.BuildCacheObject<Game>(new Game(), dataRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Game> _GetGame(SearchUsing searchUsing, object searchValue, bool getAllMetadata = true, bool followSubGames = false, bool forceRefresh = false)
|
private static Game MassageResult(Game result)
|
||||||
{
|
{
|
||||||
// check database first
|
Game? parentGame = null;
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Game", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Game", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceRefresh == true)
|
// get cover art from parent if this has no cover
|
||||||
|
if (result.Cover == null)
|
||||||
{
|
{
|
||||||
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
|
if (result.ParentGame != null)
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
{
|
||||||
case SearchUsing.id:
|
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no cover art, fetching cover art from parent game");
|
||||||
WhereClause = "where id = " + searchValue;
|
parentGame = GetGame(result.MetadataSource, (long)result.ParentGame);
|
||||||
break;
|
result.Cover = parentGame.Cover;
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Game returnValue = new Game();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
|
|
||||||
return returnValue;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
return Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames)
|
// get missing metadata from parent if this is a port
|
||||||
|
if (result.Category == HasheousClient.Models.Metadata.IGDB.Category.Port)
|
||||||
{
|
{
|
||||||
// required metadata
|
if (result.Summary == null)
|
||||||
if (Game.Cover != null)
|
|
||||||
{
|
{
|
||||||
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
if (result.ParentGame != null)
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Genres != null)
|
|
||||||
{
|
{
|
||||||
foreach (long GenreId in Game.Genres.Ids)
|
Logging.Log(Logging.LogType.Information, "Game Metadata", "Game has no summary, fetching summary from parent game");
|
||||||
{
|
result.Summary = parentGame.Summary;
|
||||||
Genre GameGenre = Genres.GetGenres(GenreId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.GameModes != null)
|
|
||||||
{
|
|
||||||
foreach (long gameModeId in Game.GameModes.Ids)
|
|
||||||
{
|
|
||||||
GameMode gameMode = GameModes.GetGame_Modes(gameModeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.MultiplayerModes != null)
|
|
||||||
{
|
|
||||||
foreach (long multiplayerModeId in Game.MultiplayerModes.Ids)
|
|
||||||
{
|
|
||||||
MultiplayerMode multiplayerMode = MultiplayerModes.GetGame_MultiplayerModes(multiplayerModeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.PlayerPerspectives != null)
|
|
||||||
{
|
|
||||||
foreach (long PerspectiveId in Game.PlayerPerspectives.Ids)
|
|
||||||
{
|
|
||||||
PlayerPerspective GamePlayPerspective = PlayerPerspectives.GetGame_PlayerPerspectives(PerspectiveId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Themes != null)
|
|
||||||
{
|
|
||||||
foreach (long ThemeId in Game.Themes.Ids)
|
|
||||||
{
|
|
||||||
Theme GameTheme = Themes.GetGame_Themes(ThemeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.AgeRatings != null)
|
|
||||||
{
|
|
||||||
foreach (long AgeRatingId in Game.AgeRatings.Ids)
|
|
||||||
{
|
|
||||||
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.ReleaseDates != null)
|
|
||||||
{
|
|
||||||
foreach (long ReleaseDateId in Game.ReleaseDates.Ids)
|
|
||||||
{
|
|
||||||
ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// optional metadata - usually downloaded as needed
|
|
||||||
if (getAllMetadata == true)
|
|
||||||
{
|
|
||||||
if (Game.AlternativeNames != null)
|
|
||||||
{
|
|
||||||
foreach (long AlternativeNameId in Game.AlternativeNames.Ids)
|
|
||||||
{
|
|
||||||
AlternativeName GameAlternativeName = AlternativeNames.GetAlternativeNames(AlternativeNameId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Artworks != null)
|
|
||||||
{
|
|
||||||
foreach (long ArtworkId in Game.Artworks.Ids)
|
|
||||||
{
|
|
||||||
Artwork GameArtwork = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (followSubGames)
|
|
||||||
{
|
|
||||||
List<long> gamesToFetch = new List<long>();
|
|
||||||
if (Game.Bundles != null) { gamesToFetch.AddRange(Game.Bundles.Ids); }
|
|
||||||
if (Game.Dlcs != null) { gamesToFetch.AddRange(Game.Dlcs.Ids); }
|
|
||||||
if (Game.Expansions != null) { gamesToFetch.AddRange(Game.Expansions.Ids); }
|
|
||||||
if (Game.ParentGame != null) { gamesToFetch.Add((long)Game.ParentGame.Id); }
|
|
||||||
//if (Game.SimilarGames != null) { gamesToFetch.AddRange(Game.SimilarGames.Ids); }
|
|
||||||
if (Game.StandaloneExpansions != null) { gamesToFetch.AddRange(Game.StandaloneExpansions.Ids); }
|
|
||||||
if (Game.VersionParent != null) { gamesToFetch.Add((long)Game.VersionParent.Id); }
|
|
||||||
|
|
||||||
foreach (long gameId in gamesToFetch)
|
|
||||||
{
|
|
||||||
Game relatedGame = GetGame(gameId, false, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Collection != null)
|
|
||||||
{
|
|
||||||
Collection GameCollection = Collections.GetCollections(Game.Collection.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.ExternalGames != null)
|
|
||||||
{
|
|
||||||
foreach (long ExternalGameId in Game.ExternalGames.Ids)
|
|
||||||
{
|
|
||||||
ExternalGame GameExternalGame = ExternalGames.GetExternalGames(ExternalGameId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Franchise != null)
|
|
||||||
{
|
|
||||||
Franchise GameFranchise = Franchises.GetFranchises(Game.Franchise.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Franchises != null)
|
|
||||||
{
|
|
||||||
foreach (long FranchiseId in Game.Franchises.Ids)
|
|
||||||
{
|
|
||||||
Franchise GameFranchise = Franchises.GetFranchises(FranchiseId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.InvolvedCompanies != null)
|
|
||||||
{
|
|
||||||
foreach (long involvedCompanyId in Game.InvolvedCompanies.Ids)
|
|
||||||
{
|
|
||||||
InvolvedCompany involvedCompany = InvolvedCompanies.GetInvolvedCompanies(involvedCompanyId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Platforms != null)
|
|
||||||
{
|
|
||||||
foreach (long PlatformId in Game.Platforms.Ids)
|
|
||||||
{
|
|
||||||
Platform GamePlatform = Platforms.GetPlatform(PlatformId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Screenshots != null)
|
|
||||||
{
|
|
||||||
foreach (long ScreenshotId in Game.Screenshots.Ids)
|
|
||||||
{
|
|
||||||
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Game.Videos != null)
|
|
||||||
{
|
|
||||||
foreach (long GameVideoId in Game.Videos.Ids)
|
|
||||||
{
|
|
||||||
GameVideo gameVideo = GamesVideos.GetGame_Videos(GameVideoId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SearchUsing
|
// populate age group data
|
||||||
|
if (result.MetadataSource == HasheousClient.Models.MetadataSources.IGDB)
|
||||||
{
|
{
|
||||||
id,
|
AgeGroups.GetAgeGroup(result);
|
||||||
slug
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Game> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
// add artificial unknown platform mapping
|
|
||||||
List<long> platformIds = new List<long>();
|
|
||||||
platformIds.Add(0);
|
|
||||||
if (result.Platforms != null)
|
|
||||||
{
|
|
||||||
if (result.Platforms.Ids != null)
|
|
||||||
{
|
|
||||||
platformIds.AddRange(result.Platforms.Ids.ToList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.Platforms = new IdentitiesOrValues<Platform>(
|
|
||||||
ids: platformIds.ToArray<long>()
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AssignAllGamesToPlatformIdZero()
|
private static bool AllowNoPlatformSearch = false;
|
||||||
{
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
|
||||||
string sql = "SELECT * FROM Game;";
|
|
||||||
DataTable gamesTable = db.ExecuteCMD(sql);
|
|
||||||
foreach (DataRow gameRow in gamesTable.Rows)
|
|
||||||
{
|
|
||||||
sql = "DELETE FROM Relation_Game_Platforms WHERE PlatformsId = 0 AND GameId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (@Id, 0);";
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
|
||||||
dbDict.Add("Id", (long)gameRow["Id"]);
|
|
||||||
db.ExecuteCMD(sql, dbDict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
|
public static Game[] SearchForGame(string SearchString, long PlatformId, SearchType searchType)
|
||||||
{
|
{
|
||||||
Task<Game[]> games = _SearchForGame(SearchString, PlatformId, searchType);
|
// search local first
|
||||||
|
Logging.Log(Logging.LogType.Information, "Game Search", "Attempting local search of type '" + searchType.ToString() + "' for " + SearchString);
|
||||||
|
Task<Game[]> games = _SearchForGameDatabase(SearchString, PlatformId, searchType);
|
||||||
|
if (games.Result.Length == 0)
|
||||||
|
{
|
||||||
|
// fall back to online search
|
||||||
|
Logging.Log(Logging.LogType.Information, "Game Search", "Falling back to remote search of type '" + searchType.ToString() + "' for " + SearchString);
|
||||||
|
games = _SearchForGameRemote(SearchString, PlatformId, searchType);
|
||||||
|
}
|
||||||
return games.Result;
|
return games.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Game[]> _SearchForGame(string SearchString, long PlatformId, SearchType searchType)
|
private static async Task<Game[]> _SearchForGameDatabase(string SearchString, long PlatformId, SearchType searchType)
|
||||||
{
|
{
|
||||||
string searchBody = "";
|
string whereClause = "";
|
||||||
string searchFields = "fields id,name,slug,platforms,summary; ";
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
bool allowSearch = true;
|
||||||
switch (searchType)
|
switch (searchType)
|
||||||
{
|
{
|
||||||
case SearchType.searchNoPlatform:
|
case SearchType.searchNoPlatform:
|
||||||
searchBody += "search \"" + SearchString + "\"; ";
|
whereClause = "MATCH(`Name`) AGAINST (@gamename)";
|
||||||
|
dbDict.Add("platformid", PlatformId);
|
||||||
|
dbDict.Add("gamename", SearchString);
|
||||||
|
|
||||||
|
allowSearch = AllowNoPlatformSearch;
|
||||||
break;
|
break;
|
||||||
case SearchType.search:
|
case SearchType.search:
|
||||||
searchBody += "search \"" + SearchString + "\"; ";
|
whereClause = "PlatformsId = @platformid AND MATCH(`Name`) AGAINST (@gamename)";
|
||||||
searchBody += "where platforms = (" + PlatformId + ");";
|
dbDict.Add("platformid", PlatformId);
|
||||||
|
dbDict.Add("gamename", SearchString);
|
||||||
break;
|
break;
|
||||||
case SearchType.wherefuzzy:
|
case SearchType.wherefuzzy:
|
||||||
searchBody += "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;";
|
whereClause = "PlatformsId = @platformid AND `Name` LIKE @gamename";
|
||||||
|
dbDict.Add("platformid", PlatformId);
|
||||||
|
dbDict.Add("gamename", "%" + SearchString + "%");
|
||||||
break;
|
break;
|
||||||
case SearchType.where:
|
case SearchType.where:
|
||||||
searchBody += "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
|
whereClause = "PlatformsId = @platformid AND `Name` = @gamename";
|
||||||
|
dbDict.Add("platformid", PlatformId);
|
||||||
|
dbDict.Add("gamename", SearchString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId AND Game.SourceId = Relation_Game_Platforms.GameSourceId WHERE " + whereClause + ";";
|
||||||
|
|
||||||
|
|
||||||
// get Game metadata
|
// get Game metadata
|
||||||
Communications comms = new Communications();
|
Game[]? results = new Game[0];
|
||||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
if (allowSearch == true)
|
||||||
|
{
|
||||||
|
List<Game> searchResults = new List<Game>();
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
Game game = new Game
|
||||||
|
{
|
||||||
|
Id = (long)row["Id"],
|
||||||
|
Name = (string)Common.ReturnValueIfNull(row["Name"], ""),
|
||||||
|
Slug = (string)Common.ReturnValueIfNull(row["Slug"], ""),
|
||||||
|
Summary = (string)Common.ReturnValueIfNull(row["Summary"], "")
|
||||||
|
};
|
||||||
|
searchResults.Add(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
results = searchResults.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<Game[]> _SearchForGameRemote(string SearchString, long PlatformId, SearchType searchType)
|
||||||
|
{
|
||||||
|
switch (Config.MetadataConfiguration.DefaultMetadataSource)
|
||||||
|
{
|
||||||
|
case HasheousClient.Models.MetadataSources.None:
|
||||||
|
return new Game[0];
|
||||||
|
case HasheousClient.Models.MetadataSources.IGDB:
|
||||||
|
if (Config.IGDB.UseHasheousProxy == false)
|
||||||
|
{
|
||||||
|
string searchBody = "";
|
||||||
|
string searchFields = "fields id,name,slug,platforms,summary; ";
|
||||||
|
bool allowSearch = true;
|
||||||
|
switch (searchType)
|
||||||
|
{
|
||||||
|
case SearchType.searchNoPlatform:
|
||||||
|
searchBody = "search \"" + SearchString + "\"; ";
|
||||||
|
|
||||||
|
allowSearch = AllowNoPlatformSearch;
|
||||||
|
break;
|
||||||
|
case SearchType.search:
|
||||||
|
searchBody = "search \"" + SearchString + "\"; where platforms = (" + PlatformId + ");";
|
||||||
|
break;
|
||||||
|
case SearchType.wherefuzzy:
|
||||||
|
searchBody = "where platforms = (" + PlatformId + ") & name ~ *\"" + SearchString + "\"*;";
|
||||||
|
break;
|
||||||
|
case SearchType.where:
|
||||||
|
searchBody = "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check search cache
|
||||||
|
Game[]? games = Communications.GetSearchCache<Game[]?>(searchFields, searchBody);
|
||||||
|
|
||||||
|
if (games == null)
|
||||||
|
{
|
||||||
|
// cache miss
|
||||||
|
// get Game metadata
|
||||||
|
Communications comms = new Communications();
|
||||||
|
Game[]? results = new Game[0];
|
||||||
|
if (allowSearch == true)
|
||||||
|
{
|
||||||
|
results = await comms.APIComm<Game>(IGDB.IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||||
|
|
||||||
|
Communications.SetSearchCache<Game[]?>(searchFields, searchBody, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return games.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
|
||||||
|
HasheousClient.Models.Metadata.IGDB.Game[] hResults = hasheous.GetMetadataProxy_SearchGame<HasheousClient.Models.Metadata.IGDB.Game>(HasheousClient.Hasheous.MetadataProvider.IGDB, PlatformId.ToString(), SearchString);
|
||||||
|
|
||||||
|
List<Game> hGames = new List<Game>();
|
||||||
|
foreach (HasheousClient.Models.Metadata.IGDB.Game hResult in hResults)
|
||||||
|
{
|
||||||
|
hGames.Add(Communications.ConvertToIGDBModel<Game>(hResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hGames.ToArray();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return new Game[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<AvailablePlatformItem> GetAvailablePlatforms(string UserId, HasheousClient.Models.MetadataSources SourceType, long GameId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = @"
|
||||||
|
SELECT DISTINCT
|
||||||
|
view_Games_Roms.MetadataMapId,
|
||||||
|
view_Games_Roms.GameId,
|
||||||
|
view_Games_Roms.PlatformId,
|
||||||
|
view_Games_Roms.UserManualLink,
|
||||||
|
Platform.`Name`,
|
||||||
|
User_RecentPlayedRoms.UserId AS MostRecentUserId,
|
||||||
|
User_RecentPlayedRoms.RomId AS MostRecentRomId,
|
||||||
|
CASE User_RecentPlayedRoms.IsMediaGroup
|
||||||
|
WHEN 0 THEN GMR.`Name`
|
||||||
|
WHEN 1 THEN view_Games_Roms.`MetadataGameName`
|
||||||
|
ELSE NULL
|
||||||
|
END AS `MostRecentRomName`,
|
||||||
|
User_RecentPlayedRoms.IsMediaGroup AS MostRecentRomIsMediaGroup,
|
||||||
|
User_GameFavouriteRoms.UserId AS FavouriteUserId,
|
||||||
|
User_GameFavouriteRoms.RomId AS FavouriteRomId,
|
||||||
|
CASE User_GameFavouriteRoms.IsMediaGroup
|
||||||
|
WHEN 0 THEN GFV.`Name`
|
||||||
|
WHEN 1 THEN view_Games_Roms.`MetadataGameName`
|
||||||
|
ELSE NULL
|
||||||
|
END AS `FavouriteRomName`,
|
||||||
|
User_GameFavouriteRoms.IsMediaGroup AS FavouriteRomIsMediaGroup
|
||||||
|
FROM
|
||||||
|
view_Games_Roms
|
||||||
|
LEFT JOIN
|
||||||
|
Platform ON view_Games_Roms.PlatformId = Platform.Id AND Platform.SourceId = view_Games_Roms.GameIdType
|
||||||
|
LEFT JOIN
|
||||||
|
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
|
||||||
|
AND User_RecentPlayedRoms.GameId = view_Games_Roms.MetadataMapId
|
||||||
|
AND User_RecentPlayedRoms.PlatformId = view_Games_Roms.PlatformId
|
||||||
|
LEFT JOIN
|
||||||
|
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||||
|
AND User_GameFavouriteRoms.GameId = view_Games_Roms.MetadataMapId
|
||||||
|
AND User_GameFavouriteRoms.PlatformId = view_Games_Roms.PlatformId
|
||||||
|
LEFT JOIN
|
||||||
|
view_Games_Roms AS GMR ON GMR.Id = User_RecentPlayedRoms.RomId
|
||||||
|
LEFT JOIN
|
||||||
|
view_Games_Roms AS GFV ON GFV.Id = User_GameFavouriteRoms.RomId
|
||||||
|
WHERE
|
||||||
|
view_Games_Roms.GameIdType = @sourcetype AND view_Games_Roms.GameId = @gameid
|
||||||
|
ORDER BY Platform.`Name`;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "sourcetype", (int)SourceType },
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", UserId }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
PlatformMapping platformMapping = new PlatformMapping();
|
||||||
|
List<AvailablePlatformItem> platforms = new List<AvailablePlatformItem>();
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
HasheousClient.Models.Metadata.IGDB.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
|
||||||
|
|
||||||
|
// get the user emulator configuration
|
||||||
|
PlatformMapping.UserEmulatorConfiguration? emulatorConfiguration = platformMapping.GetUserEmulator(UserId, GameId, (long)platform.Id);
|
||||||
|
|
||||||
|
// if no user configuration, get the platform emulator configuration
|
||||||
|
if (emulatorConfiguration == null)
|
||||||
|
{
|
||||||
|
if (platform.Id != 0)
|
||||||
|
{
|
||||||
|
Models.PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap((long)platform.Id);
|
||||||
|
if (platformMap != null)
|
||||||
|
{
|
||||||
|
emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration
|
||||||
|
{
|
||||||
|
EmulatorType = platformMap.WebEmulator.Type,
|
||||||
|
Core = platformMap.WebEmulator.Core,
|
||||||
|
EnableBIOSFiles = platformMap.EnabledBIOSHashes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if still no emulator configuration, create a blank one
|
||||||
|
if (emulatorConfiguration == null)
|
||||||
|
{
|
||||||
|
emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration
|
||||||
|
{
|
||||||
|
EmulatorType = "",
|
||||||
|
Core = "",
|
||||||
|
EnableBIOSFiles = new List<string>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
long? LastPlayedRomId = null;
|
||||||
|
bool? LastPlayedIsMediagroup = false;
|
||||||
|
string? LastPlayedRomName = null;
|
||||||
|
if (row["MostRecentRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
LastPlayedRomId = (long?)row["MostRecentRomId"];
|
||||||
|
LastPlayedIsMediagroup = (bool)row["MostRecentRomIsMediaGroup"];
|
||||||
|
if (row["MostRecentRomName"] != System.DBNull.Value)
|
||||||
|
{
|
||||||
|
LastPlayedRomName = string.IsNullOrEmpty((string?)row["MostRecentRomName"]) ? "" : (string)row["MostRecentRomName"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long? FavouriteRomId = null;
|
||||||
|
bool? FavouriteRomIsMediagroup = false;
|
||||||
|
string? FavouriteRomName = null;
|
||||||
|
if (row["FavouriteRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
FavouriteRomId = (long?)row["FavouriteRomId"];
|
||||||
|
FavouriteRomIsMediagroup = (bool)row["FavouriteRomIsMediaGroup"];
|
||||||
|
if (row["FavouriteRomName"] != System.DBNull.Value)
|
||||||
|
{
|
||||||
|
FavouriteRomName = string.IsNullOrEmpty((string?)row["FavouriteRomName"]) ? "" : (string)row["FavouriteRomName"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string? UserManualLink = null;
|
||||||
|
if (row["UserManualLink"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
UserManualLink = string.IsNullOrEmpty((string?)row["UserManualLink"]) ? "" : (string)row["UserManualLink"];
|
||||||
|
}
|
||||||
|
|
||||||
|
AvailablePlatformItem valuePair = new AvailablePlatformItem
|
||||||
|
{
|
||||||
|
Id = platform.Id,
|
||||||
|
Name = platform.Name,
|
||||||
|
MetadataMapId = (long)row["MetadataMapId"],
|
||||||
|
Category = platform.Category,
|
||||||
|
emulatorConfiguration = emulatorConfiguration,
|
||||||
|
LastPlayedRomId = LastPlayedRomId,
|
||||||
|
LastPlayedRomIsMediagroup = LastPlayedIsMediagroup,
|
||||||
|
LastPlayedRomName = LastPlayedRomName,
|
||||||
|
FavouriteRomId = FavouriteRomId,
|
||||||
|
FavouriteRomIsMediagroup = FavouriteRomIsMediagroup,
|
||||||
|
FavouriteRomName = FavouriteRomName,
|
||||||
|
UserManualLink = UserManualLink
|
||||||
|
};
|
||||||
|
platforms.Add(valuePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort platforms by the Name attribute
|
||||||
|
platforms.Sort((x, y) => x.Name.CompareTo(y.Name));
|
||||||
|
|
||||||
|
return platforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GameSetFavouriteRom(string UserId, long GameId, long PlatformId, long RomId, bool IsMediaGroup)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "DELETE FROM User_GameFavouriteRoms WHERE UserId = @userid AND GameId = @gameid AND PlatformId = @platformid; INSERT INTO User_GameFavouriteRoms (UserId, GameId, PlatformId, RomId, IsMediaGroup) VALUES (@userid, @gameid, @platformid, @romid, @ismediagroup);";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "platformid", PlatformId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GameClearFavouriteRom(string UserId, long GameId, long PlatformId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "DELETE FROM User_GameFavouriteRoms WHERE UserId = @userid AND GameId = @gameid AND PlatformId = @platformid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "platformid", PlatformId }
|
||||||
|
};
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AvailablePlatformItem : HasheousClient.Models.Metadata.IGDB.Platform
|
||||||
|
{
|
||||||
|
public long MetadataMapId { get; set; }
|
||||||
|
public PlatformMapping.UserEmulatorConfiguration emulatorConfiguration { get; set; }
|
||||||
|
public long? LastPlayedRomId { get; set; }
|
||||||
|
public bool? LastPlayedRomIsMediagroup { get; set; }
|
||||||
|
public string? LastPlayedRomName { get; set; }
|
||||||
|
public long? FavouriteRomId { get; set; }
|
||||||
|
public bool? FavouriteRomIsMediagroup { get; set; }
|
||||||
|
public string? FavouriteRomName { get; set; }
|
||||||
|
public string? UserManualLink { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public enum SearchType
|
public enum SearchType
|
||||||
{
|
{
|
||||||
where = 0,
|
where = 0,
|
||||||
@@ -369,22 +435,31 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
|
|
||||||
public class MinimalGameItem
|
public class MinimalGameItem
|
||||||
{
|
{
|
||||||
|
public MinimalGameItem()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public MinimalGameItem(Game gameObject)
|
public MinimalGameItem(Game gameObject)
|
||||||
{
|
{
|
||||||
this.Id = gameObject.Id;
|
this.Id = gameObject.Id;
|
||||||
|
this.MetadataMapId = gameObject.MetadataMapId;
|
||||||
this.Name = gameObject.Name;
|
this.Name = gameObject.Name;
|
||||||
|
this.Slug = gameObject.Slug;
|
||||||
|
this.Summary = gameObject.Summary;
|
||||||
this.TotalRating = gameObject.TotalRating;
|
this.TotalRating = gameObject.TotalRating;
|
||||||
this.TotalRatingCount = gameObject.TotalRatingCount;
|
this.TotalRatingCount = gameObject.TotalRatingCount;
|
||||||
this.Cover = gameObject.Cover;
|
this.Cover = gameObject.Cover;
|
||||||
this.Artworks = gameObject.Artworks;
|
this.Artworks = gameObject.Artworks;
|
||||||
|
this.FirstReleaseDate = gameObject.FirstReleaseDate;
|
||||||
|
|
||||||
// compile age ratings
|
// compile age ratings
|
||||||
this.AgeRatings = new List<AgeRating>();
|
this.AgeRatings = new List<object>();
|
||||||
if (gameObject.AgeRatings != null)
|
if (gameObject.AgeRatings != null)
|
||||||
{
|
{
|
||||||
foreach (long ageRatingId in gameObject.AgeRatings.Ids)
|
foreach (long ageRatingId in gameObject.AgeRatings)
|
||||||
{
|
{
|
||||||
AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId);
|
HasheousClient.Models.Metadata.IGDB.AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRating(gameObject.MetadataSource, ageRatingId);
|
||||||
if (rating != null)
|
if (rating != null)
|
||||||
{
|
{
|
||||||
this.AgeRatings.Add(rating);
|
this.AgeRatings.Add(rating);
|
||||||
@@ -394,12 +469,19 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long? Id { get; set; }
|
public long? Id { get; set; }
|
||||||
|
public long? MetadataMapId { get; set; }
|
||||||
|
public long Index { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public string Slug { get; set; }
|
||||||
|
public string Summary { get; set; }
|
||||||
public double? TotalRating { get; set; }
|
public double? TotalRating { get; set; }
|
||||||
public int? TotalRatingCount { get; set; }
|
public int? TotalRatingCount { get; set; }
|
||||||
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
|
public bool HasSavedGame { get; set; } = false;
|
||||||
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
|
public bool IsFavourite { get; set; } = false;
|
||||||
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }
|
public DateTimeOffset? FirstReleaseDate { get; set; }
|
||||||
|
public object Cover { get; set; }
|
||||||
|
public List<long> Artworks { get; set; }
|
||||||
|
public List<object> AgeRatings { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Genres
|
public class Genres
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
||||||
|
|
||||||
public Genres()
|
public Genres()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Genre? GetGenres(long? Id)
|
public static Genre? GetGenres(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Genre> RetVal = _GetGenres(SearchUsing.id, Id);
|
Genre? RetVal = Metadata.GetMetadata<Genre>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Genre GetGenres(string Slug)
|
|
||||||
{
|
|
||||||
Task<Genre> RetVal = _GetGenres(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Genre> _GetGenres(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Genre", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Genre", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Genre returnValue = new Genre();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Genre> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Genres metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Genre>(IGDBClient.Endpoints.Genres, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class InvolvedCompanies
|
public class InvolvedCompanies
|
||||||
{
|
{
|
||||||
const string fieldList = "fields *;";
|
public const string fieldList = "fields *;";
|
||||||
|
|
||||||
public InvolvedCompanies()
|
public InvolvedCompanies()
|
||||||
{
|
{
|
||||||
@@ -20,106 +19,8 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<InvolvedCompany> RetVal = _GetInvolvedCompanies(SearchUsing.id, Id);
|
InvolvedCompany? RetVal = Metadata.GetMetadata<InvolvedCompany>(HasheousClient.Models.MetadataSources.IGDB, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InvolvedCompany GetInvolvedCompanies(string Slug)
|
|
||||||
{
|
|
||||||
Task<InvolvedCompany> RetVal = _GetInvolvedCompanies(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<InvolvedCompany> _GetInvolvedCompanies(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("InvolvedCompany", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("InvolvedCompany", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
InvolvedCompany returnValue = new InvolvedCompany();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
UpdateSubClasses(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateSubClasses(InvolvedCompany involvedCompany)
|
|
||||||
{
|
|
||||||
if (involvedCompany.Company != null)
|
|
||||||
{
|
|
||||||
Company company = Companies.GetCompanies(involvedCompany.Company.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<InvolvedCompany> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get InvolvedCompanies metadata
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<InvolvedCompany>(IGDBClient.Endpoints.InvolvedCompanies, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Failure when requesting involved companies.");
|
|
||||||
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Field list: " + fieldList);
|
|
||||||
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Where clause: " + WhereClause);
|
|
||||||
Logging.Log(Logging.LogType.Critical, "Involved Companies", "Error", ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
262
gaseous-server/Classes/Metadata/Metadata.cs
Normal file
262
gaseous-server/Classes/Metadata/Metadata.cs
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace gaseous_server.Classes.Metadata
|
||||||
|
{
|
||||||
|
public class Metadata
|
||||||
|
{
|
||||||
|
#region Exception Handling
|
||||||
|
public class InvalidMetadataId : Exception
|
||||||
|
{
|
||||||
|
public InvalidMetadataId(long Id) : base("Invalid Metadata id: " + Id + " from source: " + HasheousClient.Models.MetadataSources.IGDB + " (default)")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidMetadataId(HasheousClient.Models.MetadataSources SourceType, long Id) : base("Invalid Metadata id: " + Id + " from source: " + SourceType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidMetadataId(string Id) : base("Invalid Metadata id: " + Id + " from source: " + HasheousClient.Models.MetadataSources.IGDB + " (default)")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidMetadataId(HasheousClient.Models.MetadataSources SourceType, string Id) : base("Invalid Metadata id: " + Id + " from source: " + SourceType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Get Metadata
|
||||||
|
/// <summary>
|
||||||
|
/// Get metadata from the default source
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">
|
||||||
|
/// The type of metadata to get
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="Id">
|
||||||
|
/// The id of the metadata to get
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The metadata object
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="InvalidMetadataId">
|
||||||
|
/// Thrown when the id is invalid
|
||||||
|
/// </exception>
|
||||||
|
public static T? GetMetadata<T>(long Id, Boolean ForceRefresh = false) where T : class
|
||||||
|
{
|
||||||
|
if (Id < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidMetadataId(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _GetMetadata<T>(HasheousClient.Models.MetadataSources.IGDB, Id, ForceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get metadata from the specified source
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">
|
||||||
|
/// The type of metadata to get
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="SourceType">
|
||||||
|
/// The source of the metadata
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Id">
|
||||||
|
/// The id of the metadata to get
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The metadata object
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="InvalidMetadataId">
|
||||||
|
/// Thrown when the id is invalid
|
||||||
|
/// </exception>
|
||||||
|
public static T? GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, long Id, Boolean ForceRefresh = false) where T : class
|
||||||
|
{
|
||||||
|
if (Id < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidMetadataId(SourceType, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _GetMetadata<T>(SourceType, Id, ForceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, string Slug, Boolean ForceRefresh = false) where T : class
|
||||||
|
{
|
||||||
|
return _GetMetadata<T>(SourceType, Slug, ForceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? _GetMetadata<T>(HasheousClient.Models.MetadataSources SourceType, object Id, Boolean ForceRefresh) where T : class
|
||||||
|
{
|
||||||
|
// get T type as string
|
||||||
|
string type = typeof(T).Name;
|
||||||
|
|
||||||
|
// get type of Id as string
|
||||||
|
IdType idType = Id.GetType() == typeof(long) ? IdType.Long : IdType.String;
|
||||||
|
|
||||||
|
// check cached metadata status
|
||||||
|
// if metadata is not cached or expired, get it from the source. Otherwise, return the cached metadata
|
||||||
|
Storage.CacheStatus? cacheStatus;
|
||||||
|
if (idType == IdType.Long)
|
||||||
|
{
|
||||||
|
cacheStatus = Storage.GetCacheStatus(SourceType, type, (long)Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cacheStatus = Storage.GetCacheStatus(SourceType, type, (string)Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ForceRefresh is true, set cache status to expired if it is current
|
||||||
|
if (ForceRefresh == true)
|
||||||
|
{
|
||||||
|
if (cacheStatus == Storage.CacheStatus.Current)
|
||||||
|
{
|
||||||
|
cacheStatus = Storage.CacheStatus.Expired;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the source is "none", cache status should be "current" or "not present"
|
||||||
|
if (SourceType == HasheousClient.Models.MetadataSources.None)
|
||||||
|
{
|
||||||
|
if (cacheStatus == Storage.CacheStatus.Expired)
|
||||||
|
{
|
||||||
|
cacheStatus = Storage.CacheStatus.Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T? metadata = (T)Activator.CreateInstance(typeof(T));
|
||||||
|
|
||||||
|
switch (cacheStatus)
|
||||||
|
{
|
||||||
|
case Storage.CacheStatus.Current:
|
||||||
|
if (idType == IdType.Long)
|
||||||
|
{
|
||||||
|
metadata = Storage.GetCacheValue<T>(SourceType, metadata, "Id", (long)Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadata = Storage.GetCacheValue<T>(SourceType, metadata, "Slug", (string)Id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Storage.CacheStatus.Expired:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (idType == IdType.Long)
|
||||||
|
{
|
||||||
|
metadata = GetMetadataFromServer<T>(SourceType, (long)Id).Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadata = GetMetadataFromServer<T>(SourceType, (string)Id).Result;
|
||||||
|
}
|
||||||
|
Storage.NewCacheValue<T>(SourceType, metadata, true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Fetch Metadata", "Failed to fetch metadata from source: " + SourceType + " for id: " + Id + " of type: " + type + ". Error: " + e.Message);
|
||||||
|
metadata = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Storage.CacheStatus.NotPresent:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (idType == IdType.Long)
|
||||||
|
{
|
||||||
|
metadata = GetMetadataFromServer<T>(SourceType, (long)Id).Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadata = GetMetadataFromServer<T>(SourceType, (string)Id).Result;
|
||||||
|
}
|
||||||
|
Storage.NewCacheValue<T>(SourceType, metadata, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Fetch Metadata", "Failed to fetch metadata from source: " + SourceType + " for id: " + Id + " of type: " + type + ". Error: " + e.Message);
|
||||||
|
metadata = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum IdType
|
||||||
|
{
|
||||||
|
Long,
|
||||||
|
String
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> GetMetadataFromServer<T>(HasheousClient.Models.MetadataSources SourceType, long Id) where T : class
|
||||||
|
{
|
||||||
|
// get T type as string
|
||||||
|
string type = typeof(T).Name;
|
||||||
|
|
||||||
|
if (SourceType == HasheousClient.Models.MetadataSources.None)
|
||||||
|
{
|
||||||
|
// generate a dummy object
|
||||||
|
var returnObject = (T)Activator.CreateInstance(typeof(T));
|
||||||
|
returnObject.GetType().GetProperty("Id").SetValue(returnObject, Id);
|
||||||
|
|
||||||
|
// if returnObject has a property called "name", query the metadatamap view for the name
|
||||||
|
if (returnObject.GetType().GetProperty("Name") != null)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT * FROM MetadataMap JOIN MetadataMapBridge ON MetadataMap.Id = MetadataMapBridge.ParentMapId WHERE MetadataSourceId = @id AND MetadataSourceType = 0;";
|
||||||
|
DataTable dataTable = db.ExecuteCMD(sql, new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "@id", Id }
|
||||||
|
});
|
||||||
|
if (dataTable.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
returnObject.GetType().GetProperty("Name").SetValue(returnObject, dataTable.Rows[0]["SignatureGameName"].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnObject;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get metadata from the server
|
||||||
|
Communications comms = new Communications();
|
||||||
|
var results = await comms.APIComm<T>(SourceType, (Communications.MetadataEndpoint)Enum.Parse(typeof(Communications.MetadataEndpoint), type, true), Id);
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
if (results == null)
|
||||||
|
{
|
||||||
|
throw new InvalidMetadataId(SourceType, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.FirstOrDefault<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> GetMetadataFromServer<T>(HasheousClient.Models.MetadataSources SourceType, string Id) where T : class
|
||||||
|
{
|
||||||
|
// get T type as string
|
||||||
|
string type = typeof(T).Name;
|
||||||
|
|
||||||
|
if (SourceType == HasheousClient.Models.MetadataSources.None)
|
||||||
|
{
|
||||||
|
// generate a dummy object
|
||||||
|
return (T)Activator.CreateInstance(typeof(T));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get metadata from the server
|
||||||
|
Communications comms = new Communications();
|
||||||
|
var results = await comms.APIComm<T>(SourceType, (Communications.MetadataEndpoint)Enum.Parse(typeof(Communications.MetadataEndpoint), type, true), Id);
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
if (results == null)
|
||||||
|
{
|
||||||
|
throw new InvalidMetadataId(SourceType, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.FirstOrDefault<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class MultiplayerModes
|
public class MultiplayerModes
|
||||||
{
|
{
|
||||||
const string fieldList = "fields campaigncoop,checksum,dropin,game,lancoop,offlinecoop,offlinecoopmax,offlinemax,onlinecoop,onlinecoopmax,onlinemax,platform,splitscreen,splitscreenonline;";
|
public const string fieldList = "fields campaigncoop,checksum,dropin,game,lancoop,offlinecoop,offlinecoopmax,offlinemax,onlinecoop,onlinecoopmax,onlinemax,platform,splitscreen,splitscreenonline;";
|
||||||
|
|
||||||
public MultiplayerModes()
|
public MultiplayerModes()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MultiplayerMode? GetGame_MultiplayerModes(long? Id)
|
public static MultiplayerMode? GetGame_MultiplayerModes(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<MultiplayerMode> RetVal = _GetGame_MultiplayerModes(SearchUsing.id, Id);
|
MultiplayerMode? RetVal = Metadata.GetMetadata<MultiplayerMode>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MultiplayerMode GetGame_MultiplayerModes(string Slug)
|
|
||||||
{
|
|
||||||
Task<MultiplayerMode> RetVal = _GetGame_MultiplayerModes(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<MultiplayerMode> _GetGame_MultiplayerModes(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("MultiplayerMode", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("MultiplayerMode", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiplayerMode returnValue = new MultiplayerMode();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<MultiplayerMode> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game_MultiplayerModes metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<MultiplayerMode>(IGDBClient.Endpoints.MultiplayerModes, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
using static gaseous_server.Models.PlatformMapping;
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class PlatformLogos
|
public class PlatformLogos
|
||||||
{
|
{
|
||||||
const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
|
public const string fieldList = "fields alpha_channel,animated,checksum,height,image_id,url,width;";
|
||||||
|
|
||||||
public PlatformLogos()
|
public PlatformLogos()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlatformLogo? GetPlatformLogo(long? Id, string LogoPath)
|
public static PlatformLogo? GetPlatformLogo(long? Id, HasheousClient.Models.MetadataSources SourceType = HasheousClient.Models.MetadataSources.IGDB)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,153 +21,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.id, Id, LogoPath);
|
PlatformLogo? RetVal = Metadata.GetMetadata<PlatformLogo>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlatformLogo GetPlatformLogo(string Slug, string LogoPath)
|
|
||||||
{
|
|
||||||
Task<PlatformLogo> RetVal = _GetPlatformLogo(SearchUsing.slug, Slug, LogoPath);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlatformLogo> _GetPlatformLogo(SearchUsing searchUsing, object searchValue, string LogoPath)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlatformLogo", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlatformLogo", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformLogo returnValue = new PlatformLogo();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlatformLogo?> GetObjectFromServer(string WhereClause, string LogoPath)
|
|
||||||
{
|
|
||||||
// get PlatformLogo 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class PlatformVersions
|
public class PlatformVersions
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,companies,connectivity,cpu,graphics,main_manufacturer,media,memory,name,online,os,output,platform_logo,platform_version_release_dates,resolutions,slug,sound,storage,summary,url;";
|
public const string fieldList = "fields checksum,companies,connectivity,cpu,graphics,main_manufacturer,media,memory,name,online,os,output,platform_logo,platform_version_release_dates,resolutions,slug,sound,storage,summary,url;";
|
||||||
|
|
||||||
public PlatformVersions()
|
public PlatformVersions()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform)
|
public static PlatformVersion? GetPlatformVersion(HasheousClient.Models.MetadataSources SourceType, long Id)
|
||||||
{
|
{
|
||||||
if (Id == 0)
|
if (Id == 0)
|
||||||
{
|
{
|
||||||
@@ -21,103 +20,8 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform);
|
PlatformVersion? RetVal = Metadata.GetMetadata<PlatformVersion>(SourceType, Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform)
|
|
||||||
{
|
|
||||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlatformVersion", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlatformVersion", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformVersion returnValue = new PlatformVersion();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
if (returnValue != null)
|
|
||||||
{
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
UpdateSubClasses(ParentPlatform, returnValue);
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
UpdateSubClasses(ParentPlatform, returnValue);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
return Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
|
|
||||||
{
|
|
||||||
if (platformVersion.PlatformLogo != null)
|
|
||||||
{
|
|
||||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlatformVersion?> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get PlatformVersion metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<PlatformVersion>(IGDBClient.Endpoints.PlatformVersions, fieldList, WhereClause);
|
|
||||||
if (results.Length > 0)
|
|
||||||
{
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,131 +1,71 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Platforms
|
public class Platforms
|
||||||
{
|
{
|
||||||
const string fieldList = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites;";
|
public const string fieldList = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites;";
|
||||||
|
|
||||||
public Platforms()
|
public Platforms()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Platform? GetPlatform(long Id, bool forceRefresh = false)
|
public static Platform? GetPlatform(long Id, HasheousClient.Models.MetadataSources? SourceType = null)
|
||||||
{
|
{
|
||||||
if (Id == 0)
|
HasheousClient.Models.MetadataSources Source = SourceType ?? Communications.MetadataSource;
|
||||||
|
|
||||||
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
Platform returnValue = new Platform();
|
Platform returnValue = new Platform();
|
||||||
if (Storage.GetCacheStatus("Platform", 0) == Storage.CacheStatus.NotPresent)
|
if (Storage.GetCacheStatus(Source, "Platform", 0) == Storage.CacheStatus.NotPresent)
|
||||||
{
|
{
|
||||||
returnValue = new Platform
|
returnValue = new Platform
|
||||||
{
|
{
|
||||||
Id = 0,
|
Id = 0,
|
||||||
Name = "Unknown Platform",
|
Name = "Unknown Platform",
|
||||||
Slug = "Unknown"
|
Slug = "unknown"
|
||||||
};
|
};
|
||||||
Storage.NewCacheValue(returnValue);
|
Storage.NewCacheValue(Source, returnValue);
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Storage.GetCacheValue<Platform>(returnValue, "id", 0);
|
return Storage.GetCacheValue<Platform>(Source, returnValue, "id", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
|
Platform? RetVal = new Platform();
|
||||||
return RetVal.Result;
|
RetVal = (Platform?)Storage.GetCacheValue<Platform>(HasheousClient.Models.MetadataSources.None, RetVal, "Id", (long)Id);
|
||||||
|
if (Source != HasheousClient.Models.MetadataSources.None)
|
||||||
|
{
|
||||||
|
if (RetVal == null)
|
||||||
|
{
|
||||||
|
RetVal = Metadata.GetMetadata<Platform>(Source, (long)Id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
|
public static Platform GetPlatform(string Slug)
|
||||||
{
|
{
|
||||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh);
|
// get platform id from slug - query Platform table
|
||||||
return RetVal.Result;
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string query = "SELECT Id FROM Platform WHERE slug = @slug AND SourceId = @sourceid;";
|
||||||
|
DataTable result = db.ExecuteCMD(query, new Dictionary<string, object> { { "@slug", Slug }, { "@sourceid", HasheousClient.Models.MetadataSources.IGDB } });
|
||||||
|
if (result.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
throw new Metadata.InvalidMetadataId(Slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh)
|
long Id = (long)result.Rows[0]["Id"];
|
||||||
{
|
return GetPlatform(Id);
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Platform", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Platform", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceRefresh == true)
|
|
||||||
{
|
|
||||||
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform returnValue = new Platform();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
UpdateSubClasses(returnValue);
|
|
||||||
AddPlatformMapping(returnValue);
|
|
||||||
return returnValue;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
UpdateSubClasses(returnValue);
|
|
||||||
AddPlatformMapping(returnValue);
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateSubClasses(Platform platform)
|
|
||||||
{
|
|
||||||
if (platform.Versions != null)
|
|
||||||
{
|
|
||||||
foreach (long PlatformVersionId in platform.Versions.Ids)
|
|
||||||
{
|
|
||||||
PlatformVersion platformVersion = PlatformVersions.GetPlatformVersion(PlatformVersionId, platform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platform.PlatformLogo != null)
|
|
||||||
{
|
|
||||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddPlatformMapping(Platform platform)
|
private static void AddPlatformMapping(Platform platform)
|
||||||
@@ -143,45 +83,16 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + platform.Name + " from predefined data.");
|
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + platform.Name + " from predefined data.");
|
||||||
// doesn't exist - add it
|
// doesn't exist - add it
|
||||||
item = new Models.PlatformMapping.PlatformMapItem{
|
item = new Models.PlatformMapping.PlatformMapItem
|
||||||
|
{
|
||||||
IGDBId = (long)platform.Id,
|
IGDBId = (long)platform.Id,
|
||||||
IGDBName = platform.Name,
|
IGDBName = platform.Name,
|
||||||
IGDBSlug = platform.Slug,
|
IGDBSlug = platform.Slug,
|
||||||
AlternateNames = new List<string>{ platform.AlternativeName }
|
AlternateNames = new List<string> { platform.AlternativeName }
|
||||||
};
|
};
|
||||||
Models.PlatformMapping.WritePlatformMap(item, false, true);
|
Models.PlatformMapping.WritePlatformMap(item, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Platform> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get platform metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AssignAllPlatformsToGameIdZero()
|
|
||||||
{
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
|
||||||
string sql = "SELECT * FROM Platform;";
|
|
||||||
DataTable platformsTable = db.ExecuteCMD(sql);
|
|
||||||
foreach (DataRow platformRow in platformsTable.Rows)
|
|
||||||
{
|
|
||||||
sql = "DELETE FROM Relation_Game_Platforms WHERE GameId = 0 AND PlatformsId = @Id; INSERT INTO Relation_Game_Platforms (GameId, PlatformsId) VALUES (0, @Id);";
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
|
||||||
dbDict.Add("Id", (long)platformRow["Id"]);
|
|
||||||
db.ExecuteCMD(sql, dbDict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class PlayerPerspectives
|
public class PlayerPerspectives
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
||||||
|
|
||||||
public PlayerPerspectives()
|
public PlayerPerspectives()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerPerspective? GetGame_PlayerPerspectives(long? Id)
|
public static PlayerPerspective? GetGame_PlayerPerspectives(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,90 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<PlayerPerspective> RetVal = _GetGame_PlayerPerspectives(SearchUsing.id, Id);
|
PlayerPerspective? RetVal = Metadata.GetMetadata<PlayerPerspective>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerPerspective GetGame_PlayerPerspectives(string Slug)
|
|
||||||
{
|
|
||||||
Task<PlayerPerspective> RetVal = _GetGame_PlayerPerspectives(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlayerPerspective> _GetGame_PlayerPerspectives(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlayerPerspective", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("PlayerPerspective", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerPerspective returnValue = new PlayerPerspective();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<PlayerPerspective> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game_PlayerPerspectives metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<PlayerPerspective>(IGDBClient.Endpoints.PlayerPerspectives, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class ReleaseDates
|
public class ReleaseDates
|
||||||
{
|
{
|
||||||
const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
|
public const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
|
||||||
|
|
||||||
public ReleaseDates()
|
public ReleaseDates()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReleaseDate? GetReleaseDates(long? Id)
|
public static ReleaseDate? GetReleaseDates(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,88 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.id, Id);
|
ReleaseDate? RetVal = Metadata.GetMetadata<ReleaseDate>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReleaseDate GetReleaseDates(string Slug)
|
|
||||||
{
|
|
||||||
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<ReleaseDate> _GetReleaseDates(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseDate returnValue = new ReleaseDate();
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<ReleaseDate> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get ReleaseDates metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<ReleaseDate>(IGDBClient.Endpoints.ReleaseDates, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Screenshots
|
public class Screenshots
|
||||||
{
|
{
|
||||||
const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
public const string fieldList = "fields alpha_channel,animated,checksum,game,height,image_id,url,width;";
|
||||||
|
|
||||||
public Screenshots()
|
public Screenshots()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Screenshot? GetScreenshot(long? Id, string LogoPath)
|
public static Screenshot? GetScreenshot(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,149 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.id, Id, LogoPath);
|
Screenshot? RetVal = Metadata.GetMetadata<Screenshot>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Screenshot GetScreenshot(string Slug, string LogoPath)
|
|
||||||
{
|
|
||||||
Task<Screenshot> RetVal = _GetScreenshot(SearchUsing.slug, Slug, LogoPath);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Screenshot> _GetScreenshot(SearchUsing searchUsing, object searchValue, string LogoPath)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Screenshot", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Screenshot", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Screenshot returnValue = new Screenshot();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
LogoPath = Path.Combine(LogoPath, "Screenshots");
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause, LogoPath);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
forceImageDownload = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!File.Exists(Path.Combine(LogoPath, "Screenshot.jpg"))) || forceImageDownload == 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Screenshot> GetObjectFromServer(string WhereClause, string LogoPath)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,23 +9,79 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
{
|
{
|
||||||
public class Storage
|
public class Storage
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cache status of a record
|
||||||
|
/// </summary>
|
||||||
public enum CacheStatus
|
public enum CacheStatus
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The record is not present in the database
|
||||||
|
/// </summary>
|
||||||
NotPresent,
|
NotPresent,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The record is present in the database and is current
|
||||||
|
/// </summary>
|
||||||
Current,
|
Current,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The record is present in the database but is expired
|
||||||
|
/// </summary>
|
||||||
Expired
|
Expired
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
|
/// <summary>
|
||||||
|
/// Get the cache status of a record in the database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SourceType">
|
||||||
|
/// The source of the metadata (IGDB, RAWG, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Endpoint">
|
||||||
|
/// The endpoint of the metadata (games, companies, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Slug">
|
||||||
|
/// The slug of the metadata record
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The cache status of the record
|
||||||
|
/// </returns>
|
||||||
|
public static CacheStatus GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, string Slug)
|
||||||
{
|
{
|
||||||
return _GetCacheStatus(Endpoint, "slug", Slug);
|
return _GetCacheStatus(SourceType, Endpoint, "slug", Slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CacheStatus GetCacheStatus(string Endpoint, long Id)
|
/// <summary>
|
||||||
|
/// Get the cache status of a record in the database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SourceType">
|
||||||
|
/// The source of the metadata (IGDB, RAWG, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Endpoint">
|
||||||
|
/// The endpoint of the metadata (games, companies, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Id">
|
||||||
|
/// The ID of the metadata record
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The cache status of the record
|
||||||
|
/// </returns>
|
||||||
|
public static CacheStatus GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, long Id)
|
||||||
{
|
{
|
||||||
return _GetCacheStatus(Endpoint, "id", Id);
|
return _GetCacheStatus(SourceType, Endpoint, "id", Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the cache status of a record in the database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Row">
|
||||||
|
/// The DataRow object to check
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The cache status of the record
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="Exception">
|
||||||
|
/// Thrown when the DataRow object does not contain a "lastUpdated" column
|
||||||
|
/// </exception>
|
||||||
public static CacheStatus GetCacheStatus(DataRow Row)
|
public static CacheStatus GetCacheStatus(DataRow Row)
|
||||||
{
|
{
|
||||||
if (Row.Table.Columns.Contains("lastUpdated"))
|
if (Row.Table.Columns.Contains("lastUpdated"))
|
||||||
@@ -46,13 +102,14 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
|
private static CacheStatus _GetCacheStatus(HasheousClient.Models.MetadataSources SourceType, string Endpoint, string SearchField, object SearchValue)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE SourceId = @SourceType AND " + SearchField + " = @" + SearchField;
|
||||||
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
dbDict.Add("SourceType", SourceType);
|
||||||
dbDict.Add("Endpoint", Endpoint);
|
dbDict.Add("Endpoint", Endpoint);
|
||||||
dbDict.Add(SearchField, SearchValue);
|
dbDict.Add(SearchField, SearchValue);
|
||||||
|
|
||||||
@@ -76,16 +133,41 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
|
/// <summary>
|
||||||
|
/// Add a new record to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SourceType">
|
||||||
|
/// The source of the metadata (IGDB, RAWG, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="ObjectToCache">
|
||||||
|
/// The object to cache
|
||||||
|
/// </param>
|
||||||
|
/// <param name="UpdateRecord">
|
||||||
|
/// Whether to update the record if it already exists
|
||||||
|
/// </param>
|
||||||
|
public static void NewCacheValue<T>(HasheousClient.Models.MetadataSources SourceType, T ObjectToCache, bool UpdateRecord = false)
|
||||||
{
|
{
|
||||||
// get the object type name
|
// get the object type name
|
||||||
string ObjectTypeName = ObjectToCache.GetType().Name;
|
string ObjectTypeName = ObjectToCache.GetType().Name;
|
||||||
|
|
||||||
// build dictionary
|
// build dictionary
|
||||||
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
|
Dictionary<string, object?> objectDict = new Dictionary<string, object?>
|
||||||
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
|
{
|
||||||
objectDict.Add("dateAdded", DateTime.UtcNow);
|
{ "SourceId", SourceType },
|
||||||
objectDict.Add("lastUpdated", DateTime.UtcNow);
|
{ "dateAdded", DateTime.UtcNow },
|
||||||
|
{ "lastUpdated", DateTime.UtcNow }
|
||||||
|
};
|
||||||
|
foreach (PropertyInfo property in ObjectToCache.GetType().GetProperties())
|
||||||
|
{
|
||||||
|
if (property.GetCustomAttribute<Models.NoDatabaseAttribute>() == null)
|
||||||
|
{
|
||||||
|
object? propertyValue = property.GetValue(ObjectToCache);
|
||||||
|
if (propertyValue != null)
|
||||||
|
{
|
||||||
|
objectDict.Add(property.Name, propertyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// generate sql
|
// generate sql
|
||||||
string fieldList = "";
|
string fieldList = "";
|
||||||
@@ -100,7 +182,7 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
fieldList = fieldList + key.Key;
|
fieldList = fieldList + key.Key;
|
||||||
valueList = valueList + "@" + key.Key;
|
valueList = valueList + "@" + key.Key;
|
||||||
if ((key.Key != "id") && (key.Key != "dateAdded"))
|
if ((key.Key != "id") && (key.Key != "dateAdded") && (key.Key != "SourceId"))
|
||||||
{
|
{
|
||||||
if (updateFieldValueList.Length > 0)
|
if (updateFieldValueList.Length > 0)
|
||||||
{
|
{
|
||||||
@@ -135,7 +217,13 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
||||||
objectDict[key.Key] = newObjectValue;
|
objectDict[key.Key] = newObjectValue;
|
||||||
|
|
||||||
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
|
StoreRelations(SourceType, ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||||
|
objectDict[key.Key] = newObjectValue;
|
||||||
|
|
||||||
|
StoreRelations(SourceType, ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "int32[]":
|
case "int32[]":
|
||||||
@@ -151,11 +239,11 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
string sql = "";
|
string sql = "";
|
||||||
if (UpdateRecord == false)
|
if (UpdateRecord == false)
|
||||||
{
|
{
|
||||||
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
|
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ");";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
|
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id AND SourceId = @SourceId;";
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute sql
|
// execute sql
|
||||||
@@ -163,19 +251,44 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
db.ExecuteCMD(sql, objectDict);
|
db.ExecuteCMD(sql, objectDict);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
|
/// <summary>
|
||||||
|
/// Get a record from the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">
|
||||||
|
/// The type of the object to return
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="SourceType">
|
||||||
|
/// The source of the metadata (IGDB, RAWG, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="EndpointType">
|
||||||
|
/// The type of the endpoint (games, companies, etc.)
|
||||||
|
/// </param>
|
||||||
|
/// <param name="SearchField">
|
||||||
|
/// The field to search for the record by
|
||||||
|
/// </param>
|
||||||
|
/// <param name="SearchValue">
|
||||||
|
/// The value to search for
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The object from the cache
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="Exception">
|
||||||
|
/// Thrown when no record is found that matches the search criteria
|
||||||
|
/// </exception>
|
||||||
|
public static T GetCacheValue<T>(HasheousClient.Models.MetadataSources SourceType, T? EndpointType, string SearchField, object SearchValue)
|
||||||
{
|
{
|
||||||
string Endpoint = EndpointType.GetType().Name;
|
string Endpoint = EndpointType.GetType().Name;
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
string sql = "SELECT * FROM " + Endpoint + " WHERE SourceId = @SourceType AND " + SearchField + " = @" + SearchField;
|
||||||
|
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
dbDict.Add("SourceType", SourceType);
|
||||||
dbDict.Add("Endpoint", Endpoint);
|
dbDict.Add("Endpoint", Endpoint);
|
||||||
dbDict.Add(SearchField, SearchValue);
|
dbDict.Add(SearchField, SearchValue);
|
||||||
|
|
||||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
DataTable dt = db.ExecuteCMD(sql, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromHours(8).Ticks));
|
||||||
if (dt.Rows.Count == 0)
|
if (dt.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// no data stored for this item
|
// no data stored for this item
|
||||||
@@ -192,198 +305,65 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
|
|
||||||
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
||||||
{
|
{
|
||||||
|
// copy the DataRow to EndpointType
|
||||||
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
|
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
|
||||||
{
|
{
|
||||||
if (dataRow.Table.Columns.Contains(property.Name))
|
if (property.GetCustomAttribute<Models.NoDatabaseAttribute>() == null)
|
||||||
{
|
{
|
||||||
if (dataRow[property.Name] != DBNull.Value)
|
// get the value from the DataRow with the same name as the property
|
||||||
|
if (dataRow.Table.Columns.Contains(property.Name) == true)
|
||||||
{
|
{
|
||||||
string objectTypeName = property.PropertyType.Name.ToLower().Split("`")[0];
|
object? value = dataRow[property.Name];
|
||||||
string subObjectTypeName = "";
|
|
||||||
object? objectToStore = null;
|
if (value != null && value != DBNull.Value)
|
||||||
if (objectTypeName == "nullable")
|
|
||||||
{
|
{
|
||||||
objectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[System.", "").Replace("]", "").ToLower();
|
// check the property type - if it's a list or array, deserialize it. Otherwise, just set the value
|
||||||
|
Type objectType = EndpointType.GetType();
|
||||||
|
if (objectType != null)
|
||||||
|
{
|
||||||
|
// fullname = System.Nullable`1[[System.DateTimeOffset,
|
||||||
|
string propertyTypeName = property.PropertyType.FullName.Split(",")[0];
|
||||||
|
bool isNullable = false;
|
||||||
|
if (propertyTypeName.StartsWith("System.Nullable"))
|
||||||
|
{
|
||||||
|
isNullable = true;
|
||||||
|
propertyTypeName = propertyTypeName.Split("[[")[1];
|
||||||
}
|
}
|
||||||
try
|
propertyTypeName = propertyTypeName.Split("`")[0];
|
||||||
{
|
|
||||||
switch (objectTypeName)
|
|
||||||
{
|
|
||||||
//case "boolean":
|
|
||||||
// Boolean storedBool = Convert.ToBoolean((int)dataRow[property.Name]);
|
|
||||||
// property.SetValue(EndpointType, storedBool);
|
|
||||||
// break;
|
|
||||||
case "datetimeoffset":
|
|
||||||
DateTimeOffset? storedDate = (DateTime?)dataRow[property.Name];
|
|
||||||
property.SetValue(EndpointType, storedDate);
|
|
||||||
break;
|
|
||||||
//case "nullable":
|
|
||||||
// Console.WriteLine("Nullable: " + property.PropertyType.UnderlyingSystemType);
|
|
||||||
// break;
|
|
||||||
case "identityorvalue":
|
|
||||||
subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower();
|
|
||||||
|
|
||||||
switch (subObjectTypeName)
|
switch (propertyTypeName.ToLower())
|
||||||
{
|
{
|
||||||
case "collection":
|
case "system.collections.generic.list":
|
||||||
objectToStore = new IdentityOrValue<Collection>(id: (long)dataRow[property.Name]);
|
var listArray = Newtonsoft.Json.JsonConvert.DeserializeObject<List<long>>(value.ToString());
|
||||||
|
property.SetValue(EndpointType, listArray);
|
||||||
break;
|
break;
|
||||||
case "company":
|
|
||||||
objectToStore = new IdentityOrValue<Company>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "cover":
|
|
||||||
objectToStore = new IdentityOrValue<Cover>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "franchise":
|
|
||||||
objectToStore = new IdentityOrValue<Franchise>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "game":
|
|
||||||
objectToStore = new IdentityOrValue<Game>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "platformfamily":
|
|
||||||
objectToStore = new IdentityOrValue<PlatformFamily>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "platformlogo":
|
|
||||||
objectToStore = new IdentityOrValue<PlatformLogo>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "platformversioncompany":
|
|
||||||
objectToStore = new IdentityOrValue<PlatformVersionCompany>(id: (long)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectToStore != null)
|
case "system.int32[]":
|
||||||
{
|
var int32array = Newtonsoft.Json.JsonConvert.DeserializeObject<int[]>(value.ToString());
|
||||||
property.SetValue(EndpointType, objectToStore);
|
property.SetValue(EndpointType, int32array);
|
||||||
}
|
break;
|
||||||
|
|
||||||
|
case "system.datetimeoffset":
|
||||||
|
property.SetValue(EndpointType, (DateTimeOffset)(DateTime?)value);
|
||||||
break;
|
break;
|
||||||
case "identitiesorvalues":
|
|
||||||
subObjectTypeName = property.PropertyType.UnderlyingSystemType.ToString().Split("`1")[1].Replace("[IGDB.Models.", "").Replace("]", "").ToLower();
|
|
||||||
|
|
||||||
long[] fromJsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>((string)dataRow[property.Name]);
|
|
||||||
|
|
||||||
switch (subObjectTypeName)
|
|
||||||
{
|
|
||||||
case "agerating":
|
|
||||||
objectToStore = new IdentitiesOrValues<AgeRating>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "alternativename":
|
|
||||||
objectToStore = new IdentitiesOrValues<AlternativeName>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "artwork":
|
|
||||||
objectToStore = new IdentitiesOrValues<Artwork>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "ageratingcontentdescription":
|
|
||||||
objectToStore = new IdentitiesOrValues<AgeRatingContentDescription>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "game":
|
|
||||||
objectToStore = new IdentitiesOrValues<Game>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "externalgame":
|
|
||||||
objectToStore = new IdentitiesOrValues<ExternalGame>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "franchise":
|
|
||||||
objectToStore = new IdentitiesOrValues<Franchise>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "gameengine":
|
|
||||||
objectToStore = new IdentitiesOrValues<GameEngine>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "gamemode":
|
|
||||||
objectToStore = new IdentitiesOrValues<GameMode>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "gamevideo":
|
|
||||||
objectToStore = new IdentitiesOrValues<GameVideo>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "genre":
|
|
||||||
objectToStore = new IdentitiesOrValues<Genre>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "involvedcompany":
|
|
||||||
objectToStore = new IdentitiesOrValues<InvolvedCompany>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "multiplayermode":
|
|
||||||
objectToStore = new IdentitiesOrValues<MultiplayerMode>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "platform":
|
|
||||||
objectToStore = new IdentitiesOrValues<Platform>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "platformversion":
|
|
||||||
objectToStore = new IdentitiesOrValues<PlatformVersion>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "platformwebsite":
|
|
||||||
objectToStore = new IdentitiesOrValues<PlatformWebsite>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "platformversioncompany":
|
|
||||||
objectToStore = new IdentitiesOrValues<PlatformVersionCompany>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "platformversionreleasedate":
|
|
||||||
objectToStore = new IdentitiesOrValues<PlatformVersionReleaseDate>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "playerperspective":
|
|
||||||
objectToStore = new IdentitiesOrValues<PlayerPerspective>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "releasedate":
|
|
||||||
objectToStore = new IdentitiesOrValues<ReleaseDate>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "screenshot":
|
|
||||||
objectToStore = new IdentitiesOrValues<Screenshot>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "theme":
|
|
||||||
objectToStore = new IdentitiesOrValues<Theme>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
case "website":
|
|
||||||
objectToStore = new IdentitiesOrValues<Website>(ids: fromJsonObject);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectToStore != null)
|
|
||||||
{
|
|
||||||
property.SetValue(EndpointType, objectToStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "int32[]":
|
|
||||||
Int32[] fromJsonObject_int32Array = Newtonsoft.Json.JsonConvert.DeserializeObject<Int32[]>((string)dataRow[property.Name]);
|
|
||||||
if (fromJsonObject_int32Array != null)
|
|
||||||
{
|
|
||||||
property.SetValue(EndpointType, fromJsonObject_int32Array);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "[igdb.models.category":
|
|
||||||
property.SetValue(EndpointType, (Category)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.gamestatus":
|
|
||||||
property.SetValue(EndpointType, (GameStatus)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.ageratingcategory":
|
|
||||||
property.SetValue(EndpointType, (AgeRatingCategory)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.ageratingcontentdescriptioncategory":
|
|
||||||
property.SetValue(EndpointType, (AgeRatingContentDescriptionCategory)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.ageratingtitle":
|
|
||||||
property.SetValue(EndpointType, (AgeRatingTitle)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.externalcategory":
|
|
||||||
property.SetValue(EndpointType, (ExternalCategory)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.startdatecategory":
|
|
||||||
property.SetValue(EndpointType, (StartDateCategory)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.releasedateregion":
|
|
||||||
property.SetValue(EndpointType, (ReleaseDateRegion)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
case "[igdb.models.releasedatecategory":
|
|
||||||
property.SetValue(EndpointType, (ReleaseDateCategory)dataRow[property.Name]);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
property.SetValue(EndpointType, dataRow[property.Name]);
|
// check if property is an enum
|
||||||
|
if (property.PropertyType.IsEnum)
|
||||||
|
{
|
||||||
|
property.SetValue(EndpointType, Enum.Parse(property.PropertyType, value.ToString()));
|
||||||
|
}
|
||||||
|
else if (Common.IsNullableEnum(property.PropertyType))
|
||||||
|
{
|
||||||
|
property.SetValue(EndpointType, Enum.Parse(Nullable.GetUnderlyingType(property.PropertyType), value.ToString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
property.SetValue(EndpointType, value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Error occurred in column " + property.Name);
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +372,7 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
return EndpointType;
|
return EndpointType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StoreRelations(string PrimaryTable, string SecondaryTable, long ObjectId, string Relations)
|
private static void StoreRelations(HasheousClient.Models.MetadataSources SourceType, string PrimaryTable, string SecondaryTable, long ObjectId, string Relations)
|
||||||
{
|
{
|
||||||
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
|
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
@@ -401,7 +381,13 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
if (data.Rows.Count == 0)
|
if (data.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// table doesn't exist, create it
|
// table doesn't exist, create it
|
||||||
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
|
sql = @"
|
||||||
|
CREATE TABLE
|
||||||
|
`" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + @"`
|
||||||
|
(`" + PrimaryTable + @"SourceId` INT NOT NULL,
|
||||||
|
`" + PrimaryTable + @"Id` BIGINT NOT NULL,
|
||||||
|
`" + SecondaryTable + @"Id` BIGINT NOT NULL,
|
||||||
|
PRIMARY KEY (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
|
||||||
db.ExecuteCMD(sql);
|
db.ExecuteCMD(sql);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -417,14 +403,47 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
long[] RelationValues = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>(Relations);
|
long[] RelationValues = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>(Relations);
|
||||||
foreach (long RelationValue in RelationValues)
|
foreach (long RelationValue in RelationValues)
|
||||||
{
|
{
|
||||||
sql = "INSERT INTO " + TableName + " (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`) VALUES (@objectid, @relationvalue);";
|
sql = "INSERT INTO " + TableName + " (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`) VALUES (@sourceid, @objectid, @relationvalue);";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("objectid", ObjectId);
|
{
|
||||||
dbDict.Add("relationvalue", RelationValue);
|
{ "sourceid", SourceType },
|
||||||
|
{ "objectid", ObjectId },
|
||||||
|
{ "relationvalue", RelationValue }
|
||||||
|
};
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CreateRelationsTables<T>()
|
||||||
|
{
|
||||||
|
string PrimaryTable = typeof(T).Name;
|
||||||
|
foreach (PropertyInfo property in typeof(T).GetProperties())
|
||||||
|
{
|
||||||
|
string SecondaryTable = property.Name;
|
||||||
|
|
||||||
|
if (property.PropertyType.Name == "IdentitiesOrValues`1")
|
||||||
|
{
|
||||||
|
|
||||||
|
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT * FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "' AND table_name = '" + TableName + "';";
|
||||||
|
DataTable data = db.ExecuteCMD(sql);
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
// table doesn't exist, create it
|
||||||
|
sql = @"
|
||||||
|
CREATE TABLE
|
||||||
|
`" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + @"`
|
||||||
|
(`" + PrimaryTable + @"SourceId` INT NOT NULL,
|
||||||
|
`" + PrimaryTable + @"Id` BIGINT NOT NULL,
|
||||||
|
`" + SecondaryTable + @"Id` BIGINT NOT NULL,
|
||||||
|
PRIMARY KEY (`" + PrimaryTable + "SourceId`, `" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
|
||||||
|
db.ExecuteCMD(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class MemoryCacheObject
|
private class MemoryCacheObject
|
||||||
{
|
{
|
||||||
public object Object { get; set; }
|
public object Object { get; set; }
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using IGDB;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using IGDB.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace gaseous_server.Classes.Metadata
|
namespace gaseous_server.Classes.Metadata
|
||||||
{
|
{
|
||||||
public class Themes
|
public class Themes
|
||||||
{
|
{
|
||||||
const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
public const string fieldList = "fields checksum,created_at,name,slug,updated_at,url;";
|
||||||
|
|
||||||
public Themes()
|
public Themes()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Theme? GetGame_Themes(long? Id)
|
public static Theme? GetGame_Themes(HasheousClient.Models.MetadataSources SourceType, long? Id)
|
||||||
{
|
{
|
||||||
if ((Id == 0) || (Id == null))
|
if ((Id == 0) || (Id == null))
|
||||||
{
|
{
|
||||||
@@ -21,90 +20,10 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Task<Theme> RetVal = _GetGame_Themes(SearchUsing.id, Id);
|
Theme? RetVal = Metadata.GetMetadata<Theme>(SourceType, (long)Id, false);
|
||||||
return RetVal.Result;
|
return RetVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Theme GetGame_Themes(string Slug)
|
|
||||||
{
|
|
||||||
Task<Theme> RetVal = _GetGame_Themes(SearchUsing.slug, Slug);
|
|
||||||
return RetVal.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Theme> _GetGame_Themes(SearchUsing searchUsing, object searchValue)
|
|
||||||
{
|
|
||||||
// check database first
|
|
||||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
|
||||||
if (searchUsing == SearchUsing.id)
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Theme", (long)searchValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cacheStatus = Storage.GetCacheStatus("Theme", (string)searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up where clause
|
|
||||||
string WhereClause = "";
|
|
||||||
switch (searchUsing)
|
|
||||||
{
|
|
||||||
case SearchUsing.id:
|
|
||||||
WhereClause = "where id = " + searchValue;
|
|
||||||
break;
|
|
||||||
case SearchUsing.slug:
|
|
||||||
WhereClause = "where slug = " + searchValue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid search type");
|
|
||||||
}
|
|
||||||
|
|
||||||
Theme returnValue = new Theme();
|
|
||||||
bool forceImageDownload = false;
|
|
||||||
switch (cacheStatus)
|
|
||||||
{
|
|
||||||
case Storage.CacheStatus.NotPresent:
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue);
|
|
||||||
forceImageDownload = true;
|
|
||||||
break;
|
|
||||||
case Storage.CacheStatus.Expired:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
returnValue = await GetObjectFromServer(WhereClause);
|
|
||||||
Storage.NewCacheValue(returnValue, true);
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
|
||||||
return Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
|
|
||||||
}
|
|
||||||
case Storage.CacheStatus.Current:
|
|
||||||
returnValue = Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("How did you get here?");
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum SearchUsing
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Theme> GetObjectFromServer(string WhereClause)
|
|
||||||
{
|
|
||||||
// get Game_Themes metadata
|
|
||||||
Communications comms = new Communications();
|
|
||||||
var results = await comms.APIComm<Theme>(IGDBClient.Endpoints.Themes, fieldList, WhereClause);
|
|
||||||
var result = results.First();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,354 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
using gaseous_server.Models;
|
using gaseous_server.Models;
|
||||||
|
using HasheousClient.Models;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
public class MetadataManagement : QueueItemStatus
|
public class MetadataManagement : QueueItemStatus
|
||||||
{
|
{
|
||||||
|
private static bool Processing = false;
|
||||||
|
|
||||||
|
public enum MetadataMapSupportDataTypes
|
||||||
|
{
|
||||||
|
UserManualLink
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new metadata map, if one with the same platformId and name does not already exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="platformId">
|
||||||
|
/// The ID of the platform to which the metadata map belongs.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="name">
|
||||||
|
/// The name of the metadata map.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The ID of the new metadata map, or the ID of the existing metadata map if one already exists.
|
||||||
|
/// </returns>
|
||||||
|
public static MetadataMap? NewMetadataMap(long platformId, string name)
|
||||||
|
{
|
||||||
|
if (Processing == true)
|
||||||
|
{
|
||||||
|
// loop until processing = false
|
||||||
|
while (Processing == true)
|
||||||
|
{
|
||||||
|
System.Threading.Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Processing = true;
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@platformId", platformId },
|
||||||
|
{ "@name", name }
|
||||||
|
};
|
||||||
|
DataTable dt = new DataTable();
|
||||||
|
|
||||||
|
// check if the metadata map already exists
|
||||||
|
MetadataMap? existingMetadataMap = GetMetadataMap(platformId, name);
|
||||||
|
if (existingMetadataMap != null)
|
||||||
|
{
|
||||||
|
Processing = false;
|
||||||
|
return existingMetadataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the metadata map
|
||||||
|
sql = "INSERT INTO MetadataMap (PlatformId, SignatureGameName) VALUES (@platformId, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
long metadataMapId = (long)dt.Rows[0][0];
|
||||||
|
|
||||||
|
// create dummy game metadata item and capture id
|
||||||
|
sql = "INSERT INTO Game (SourceId, Name, dateAdded, lastUpdated) VALUES (@sourceid, @name, @dateadded, @lastupdated); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
|
dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@sourceid", HasheousClient.Models.MetadataSources.None },
|
||||||
|
{ "@name", name },
|
||||||
|
{ "@dateadded", DateTime.UtcNow },
|
||||||
|
{ "@lastupdated", DateTime.UtcNow }
|
||||||
|
};
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
long gameId = (long)dt.Rows[0][0];
|
||||||
|
|
||||||
|
// add default metadata sources
|
||||||
|
AddMetadataMapItem(metadataMapId, HasheousClient.Models.MetadataSources.None, gameId, true);
|
||||||
|
|
||||||
|
// return the new metadata map
|
||||||
|
MetadataMap RetVal = GetMetadataMap(metadataMapId);
|
||||||
|
Processing = false;
|
||||||
|
return RetVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a metadata map item to the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadataMapId">
|
||||||
|
/// The ID of the metadata map to which the item belongs.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="sourceType">
|
||||||
|
/// The type of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="sourceId">
|
||||||
|
/// The ID of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="preferred">
|
||||||
|
/// Whether the metadata source is preferred.
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the metadata source is preferred, all other metadata sources for the same metadata map will be set to not preferred.
|
||||||
|
/// </remarks>
|
||||||
|
public static void AddMetadataMapItem(long metadataMapId, HasheousClient.Models.MetadataSources sourceType, long sourceId, bool preferred)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@metadataMapId", metadataMapId },
|
||||||
|
{ "@sourceType", sourceType },
|
||||||
|
{ "@sourceId", sourceId },
|
||||||
|
{ "@preferred", preferred },
|
||||||
|
{ "@processedatimport", false }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (preferred == true)
|
||||||
|
{
|
||||||
|
// set all other items to not preferred
|
||||||
|
sql = "UPDATE MetadataMapBridge SET Preferred = 0 WHERE ParentMapId = @metadataMapId;";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = "INSERT INTO MetadataMapBridge (ParentMapId, MetadataSourceType, MetadataSourceId, Preferred, ProcessedAtImport) VALUES (@metadataMapId, @sourceType, @sourceId, @preferred, @processedatimport);";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateMetadataMapItem(long metadataMapId, HasheousClient.Models.MetadataSources SourceType, long sourceId, bool? preferred = null)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@metadataMapId", metadataMapId },
|
||||||
|
{ "@sourceType", SourceType },
|
||||||
|
{ "@sourceId", sourceId },
|
||||||
|
{ "@preferred", preferred }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (preferred == true)
|
||||||
|
{
|
||||||
|
// set all other items to not preferred
|
||||||
|
sql = "UPDATE MetadataMapBridge SET Preferred = 0 WHERE ParentMapId = @metadataMapId; UPDATE MetadataMapBridge SET MetadataSourceId = @sourceId, Preferred = @preferred WHERE ParentMapId = @metadataMapId AND MetadataSourceType = @sourceType;";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sql = "UPDATE MetadataMapBridge SET MetadataSourceId = @sourceId WHERE ParentMapId = @metadataMapId AND MetadataSourceType = @sourceType;";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a metadata map from the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="platformId">
|
||||||
|
/// The ID of the platform to which the metadata map belongs.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="name">
|
||||||
|
/// The name of the metadata map.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The metadata map, or null if it does not exist.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method will return the first metadata map found with the given platformId and name.
|
||||||
|
/// </remarks>
|
||||||
|
public static MetadataMap? GetMetadataMap(long platformId, string name)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@platformId", platformId },
|
||||||
|
{ "@name", name.Trim() }
|
||||||
|
};
|
||||||
|
DataTable dt = new DataTable();
|
||||||
|
|
||||||
|
sql = "SELECT Id FROM MetadataMap WHERE PlatformId = @platformId AND SignatureGameName = @name;";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
return GetMetadataMap((long)dt.Rows[0]["Id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a metadata map from the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadataMapId">
|
||||||
|
/// The ID of the metadata map.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The metadata map, or null if it does not exist.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method will return the metadata map with the given ID.
|
||||||
|
/// </remarks>
|
||||||
|
public static MetadataMap? GetMetadataMap(long metadataMapId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@metadataMapId", metadataMapId }
|
||||||
|
};
|
||||||
|
DataTable dt = new DataTable();
|
||||||
|
|
||||||
|
sql = "SELECT * FROM MetadataMap WHERE Id = @metadataMapId;";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
MetadataMap metadataMap = new MetadataMap()
|
||||||
|
{
|
||||||
|
Id = (long)dt.Rows[0]["Id"],
|
||||||
|
PlatformId = (long)dt.Rows[0]["PlatformId"],
|
||||||
|
SignatureGameName = dt.Rows[0]["SignatureGameName"].ToString(),
|
||||||
|
MetadataMapItems = new List<MetadataMap.MetadataMapItem>()
|
||||||
|
};
|
||||||
|
|
||||||
|
sql = "SELECT * FROM MetadataMapBridge WHERE ParentMapId = @metadataMapId;";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
foreach (DataRow dr in dt.Rows)
|
||||||
|
{
|
||||||
|
MetadataMap.MetadataMapItem metadataMapItem = new MetadataMap.MetadataMapItem()
|
||||||
|
{
|
||||||
|
SourceType = (HasheousClient.Models.MetadataSources)dr["MetadataSourceType"],
|
||||||
|
SourceId = (long)dr["MetadataSourceId"],
|
||||||
|
Preferred = (bool)dr["Preferred"]
|
||||||
|
};
|
||||||
|
|
||||||
|
metadataMap.MetadataMapItems.Add(metadataMapItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetMetadataSupportData(long metadataMapId, MetadataMapSupportDataTypes dataType, string data)
|
||||||
|
{
|
||||||
|
// verify the metadata map exists
|
||||||
|
MetadataMap? metadataMap = GetMetadataMap(metadataMapId);
|
||||||
|
if (metadataMap == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@metadataMapId", metadataMapId },
|
||||||
|
{ "@data", data }
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (dataType)
|
||||||
|
{
|
||||||
|
case MetadataMapSupportDataTypes.UserManualLink:
|
||||||
|
sql = "UPDATE MetadataMap SET UserManualLink = @data WHERE Id = @metadataMapId;";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the MetadataMapItem for the provided metadata source, and source id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceType">
|
||||||
|
/// The type of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="sourceId">
|
||||||
|
/// The ID of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The MetadataMapItem, or null if it does not exist.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method will return the MetadataMapItem with the given sourceType and sourceId.
|
||||||
|
/// </remarks>
|
||||||
|
public static MetadataMap.MetadataMapItem? GetMetadataMapFromSourceId(HasheousClient.Models.MetadataSources sourceType, long sourceId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@sourceType", sourceType },
|
||||||
|
{ "@sourceId", sourceId }
|
||||||
|
};
|
||||||
|
DataTable dt = new DataTable();
|
||||||
|
|
||||||
|
sql = "SELECT * FROM MetadataMapBridge WHERE MetadataSourceType = @sourceType AND MetadataSourceId = @sourceId;";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
MetadataMap.MetadataMapItem metadataMapItem = new MetadataMap.MetadataMapItem()
|
||||||
|
{
|
||||||
|
SourceType = (HasheousClient.Models.MetadataSources)dt.Rows[0]["MetadataSourceType"],
|
||||||
|
SourceId = (long)dt.Rows[0]["MetadataSourceId"],
|
||||||
|
Preferred = (bool)dt.Rows[0]["Preferred"]
|
||||||
|
};
|
||||||
|
|
||||||
|
return metadataMapItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Id of the MetadataMap for the provided metadata source, and source id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceType">
|
||||||
|
/// The type of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="sourceId">
|
||||||
|
/// The ID of the metadata source.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// The ID of the MetadataMap, or null if it does not exist.
|
||||||
|
/// </returns>
|
||||||
|
public static long? GetMetadataMapIdFromSourceId(HasheousClient.Models.MetadataSources sourceType, long sourceId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "@sourceType", sourceType },
|
||||||
|
{ "@sourceId", sourceId }
|
||||||
|
};
|
||||||
|
DataTable dt = new DataTable();
|
||||||
|
|
||||||
|
sql = "SELECT * FROM MetadataMapBridge WHERE MetadataSourceType = @sourceType AND MetadataSourceId = @sourceId;";
|
||||||
|
dt = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
return (long)dt.Rows[0]["ParentMapId"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void RefreshMetadata(bool forceRefresh = false)
|
public void RefreshMetadata(bool forceRefresh = false)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
@@ -15,7 +358,7 @@ namespace gaseous_server.Classes
|
|||||||
// disabling forceRefresh
|
// disabling forceRefresh
|
||||||
forceRefresh = false;
|
forceRefresh = false;
|
||||||
|
|
||||||
// update platforms
|
// update platform metadata
|
||||||
sql = "SELECT Id, `Name` FROM Platform;";
|
sql = "SELECT Id, `Name` FROM Platform;";
|
||||||
dt = db.ExecuteCMD(sql);
|
dt = db.ExecuteCMD(sql);
|
||||||
|
|
||||||
@@ -27,7 +370,23 @@ namespace gaseous_server.Classes
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
|
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")");
|
||||||
Metadata.Platforms.GetPlatform((long)dr["id"], true);
|
|
||||||
|
HasheousClient.Models.MetadataSources metadataSource = HasheousClient.Models.MetadataSources.None;
|
||||||
|
|
||||||
|
// fetch the platform metadata
|
||||||
|
Platform platform = Metadata.Platforms.GetPlatform((long)dr["id"], metadataSource);
|
||||||
|
|
||||||
|
// fetch the platform metadata from Hasheous
|
||||||
|
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
|
||||||
|
{
|
||||||
|
Communications.PopulateHasheousPlatformData((long)dr["id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// force platformLogo refresh
|
||||||
|
if (platform.PlatformLogo != null)
|
||||||
|
{
|
||||||
|
Metadata.PlatformLogos.GetPlatformLogo(platform.PlatformLogo, metadataSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -38,7 +397,68 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
ClearStatus();
|
ClearStatus();
|
||||||
|
|
||||||
// update games
|
// update rom signatures - only valid if Haseheous is enabled
|
||||||
|
if (Config.MetadataConfiguration.SignatureSource == MetadataModel.SignatureSources.Hasheous)
|
||||||
|
{
|
||||||
|
// get all ROMs in the database
|
||||||
|
sql = "SELECT * FROM view_Games_Roms;";
|
||||||
|
dt = db.ExecuteCMD(sql);
|
||||||
|
|
||||||
|
StatusCounter = 1;
|
||||||
|
foreach (DataRow dr in dt.Rows)
|
||||||
|
{
|
||||||
|
SetStatus(StatusCounter, dt.Rows.Count, "Refreshing signature for ROM " + dr["Name"]);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing signature for ROM " + dr["Name"] + " (" + dr["Id"] + ")");
|
||||||
|
|
||||||
|
// get the hash of the ROM from the datarow
|
||||||
|
string? md5 = dr["MD5"] == DBNull.Value ? null : dr["MD5"].ToString();
|
||||||
|
string? sha1 = dr["SHA1"] == DBNull.Value ? null : dr["SHA1"].ToString();
|
||||||
|
Common.hashObject hash = new Common.hashObject();
|
||||||
|
if (md5 != null)
|
||||||
|
{
|
||||||
|
hash.md5hash = md5;
|
||||||
|
}
|
||||||
|
if (sha1 != null)
|
||||||
|
{
|
||||||
|
hash.sha1hash = sha1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the library for the ROM
|
||||||
|
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)dr["LibraryId"]);
|
||||||
|
|
||||||
|
// get the signature for the ROM
|
||||||
|
FileInfo fi = new FileInfo(dr["Path"].ToString());
|
||||||
|
FileSignature fileSignature = new FileSignature();
|
||||||
|
gaseous_server.Models.Signatures_Games signature = fileSignature.GetFileSignature(library, hash, fi, fi.FullName);
|
||||||
|
|
||||||
|
// validate the signature - if it is invalid, skip the rest of the loop
|
||||||
|
// validation rules: 1) signature must not be null, 2) signature must have a platform ID
|
||||||
|
if (signature == null || signature.Flags.PlatformId == null)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Signature for " + dr["RomName"] + " is invalid - skipping metadata refresh");
|
||||||
|
StatusCounter += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the signature in the database
|
||||||
|
Platform? signaturePlatform = Metadata.Platforms.GetPlatform((long)signature.Flags.PlatformId);
|
||||||
|
ImportGame.StoreGame(library, hash, signature, signaturePlatform, fi.FullName, (long)dr["Id"], false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["RomName"], ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusCounter += 1;
|
||||||
|
}
|
||||||
|
ClearStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update game metadata
|
||||||
if (forceRefresh == true)
|
if (forceRefresh == true)
|
||||||
{
|
{
|
||||||
// when forced, only update games with ROMs for
|
// when forced, only update games with ROMs for
|
||||||
@@ -47,7 +467,7 @@ namespace gaseous_server.Classes
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// when run normally, update all games (since this will honour cache timeouts)
|
// when run normally, update all games (since this will honour cache timeouts)
|
||||||
sql = "SELECT Id, `Name` FROM Game;";
|
sql = "SELECT DISTINCT MetadataSourceId AS `Id`, MetadataSourceType AS `GameIdType`, SignatureGameName AS `Name` FROM gaseous.view_MetadataMap;";
|
||||||
}
|
}
|
||||||
dt = db.ExecuteCMD(sql);
|
dt = db.ExecuteCMD(sql);
|
||||||
|
|
||||||
@@ -58,8 +478,111 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
|
MetadataSources metadataSource;
|
||||||
Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
|
if (dr["GameIdType"] == DBNull.Value)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Unable to refresh metadata for game " + dr["name"] + " (" + dr["id"] + ") - no source type specified");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadataSource = (MetadataSources)Enum.Parse(typeof(MetadataSources), dr["GameIdType"].ToString());
|
||||||
|
|
||||||
|
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "(" + StatusCounter + "/" + dt.Rows.Count + "): Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ") using source " + metadataSource.ToString());
|
||||||
|
HasheousClient.Models.Metadata.IGDB.Game game = Metadata.Games.GetGame(metadataSource, (long)dr["id"]);
|
||||||
|
|
||||||
|
// get supporting metadata
|
||||||
|
if (game != null)
|
||||||
|
{
|
||||||
|
if (game.AgeRatings != null)
|
||||||
|
{
|
||||||
|
foreach (long ageRatingId in game.AgeRatings)
|
||||||
|
{
|
||||||
|
AgeRating ageRating = Metadata.AgeRatings.GetAgeRating(metadataSource, ageRatingId);
|
||||||
|
if (ageRating.ContentDescriptions != null)
|
||||||
|
{
|
||||||
|
foreach (long ageRatingContentDescriptionId in ageRating.ContentDescriptions)
|
||||||
|
{
|
||||||
|
Metadata.AgeRatingContentDescriptions.GetAgeRatingContentDescriptions(metadataSource, ageRatingContentDescriptionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.AlternativeNames != null)
|
||||||
|
{
|
||||||
|
foreach (long alternateNameId in game.AlternativeNames)
|
||||||
|
{
|
||||||
|
Metadata.AlternativeNames.GetAlternativeNames(metadataSource, alternateNameId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Artworks != null)
|
||||||
|
{
|
||||||
|
foreach (long artworkId in game.Artworks)
|
||||||
|
{
|
||||||
|
Metadata.Artworks.GetArtwork(metadataSource, artworkId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Cover != null)
|
||||||
|
{
|
||||||
|
Metadata.Covers.GetCover(metadataSource, (long?)game.Cover);
|
||||||
|
}
|
||||||
|
if (game.GameModes != null)
|
||||||
|
{
|
||||||
|
foreach (long gameModeId in game.GameModes)
|
||||||
|
{
|
||||||
|
Metadata.GameModes.GetGame_Modes(metadataSource, gameModeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Genres != null)
|
||||||
|
{
|
||||||
|
foreach (long genreId in game.Genres)
|
||||||
|
{
|
||||||
|
Metadata.Genres.GetGenres(metadataSource, genreId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Videos != null)
|
||||||
|
{
|
||||||
|
foreach (long gameVideoId in game.Videos)
|
||||||
|
{
|
||||||
|
Metadata.GamesVideos.GetGame_Videos(metadataSource, gameVideoId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.MultiplayerModes != null)
|
||||||
|
{
|
||||||
|
foreach (long multiplayerModeId in game.MultiplayerModes)
|
||||||
|
{
|
||||||
|
Metadata.MultiplayerModes.GetGame_MultiplayerModes(metadataSource, multiplayerModeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.PlayerPerspectives != null)
|
||||||
|
{
|
||||||
|
foreach (long playerPerspectiveId in game.PlayerPerspectives)
|
||||||
|
{
|
||||||
|
Metadata.PlayerPerspectives.GetGame_PlayerPerspectives(metadataSource, playerPerspectiveId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.ReleaseDates != null)
|
||||||
|
{
|
||||||
|
foreach (long releaseDateId in game.ReleaseDates)
|
||||||
|
{
|
||||||
|
Metadata.ReleaseDates.GetReleaseDates(metadataSource, releaseDateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Screenshots != null)
|
||||||
|
{
|
||||||
|
foreach (long screenshotId in game.Screenshots)
|
||||||
|
{
|
||||||
|
Metadata.Screenshots.GetScreenshot(metadataSource, screenshotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (game.Themes != null)
|
||||||
|
{
|
||||||
|
foreach (long themeId in game.Themes)
|
||||||
|
{
|
||||||
|
Metadata.Themes.GetGame_Themes(metadataSource, themeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ using System;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using gaseous_signature_parser.models.RomSignatureObject;
|
using gaseous_signature_parser.models.RomSignatureObject;
|
||||||
using Microsoft.VisualBasic;
|
using Microsoft.VisualBasic;
|
||||||
using IGDB.Models;
|
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using SharpCompress.Archives;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
@@ -13,7 +16,7 @@ namespace gaseous_server.Classes
|
|||||||
public class InvalidMediaGroupId : Exception
|
public class InvalidMediaGroupId : Exception
|
||||||
{
|
{
|
||||||
public InvalidMediaGroupId(long Id) : base("Unable to find media group by id " + Id)
|
public InvalidMediaGroupId(long Id) : base("Unable to find media group by id " + Id)
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameRomMediaGroupItem CreateMediaGroup(long GameId, long PlatformId, List<long> RomIds)
|
public static GameRomMediaGroupItem CreateMediaGroup(long GameId, long PlatformId, List<long> RomIds)
|
||||||
@@ -55,12 +58,15 @@ namespace gaseous_server.Classes
|
|||||||
return GetMediaGroup(mgId);
|
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);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomMediaGroup WHERE 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>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("id", Id);
|
{
|
||||||
|
{ "id", Id },
|
||||||
|
{ "userid", userid }
|
||||||
|
};
|
||||||
|
|
||||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
@@ -75,12 +81,43 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId)
|
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId, string userid = "", long? PlatformId = null)
|
||||||
{
|
{
|
||||||
|
string PlatformWhereClause = "";
|
||||||
|
if (PlatformId != null)
|
||||||
|
{
|
||||||
|
PlatformWhereClause = " AND RomMediaGroup.PlatformId=@platformid";
|
||||||
|
}
|
||||||
|
|
||||||
|
string UserFields = "";
|
||||||
|
string UserJoin = "";
|
||||||
|
if (userid.Length > 0)
|
||||||
|
{
|
||||||
|
UserFields = ", User_RecentPlayedRoms.RomId AS MostRecentRomId, User_RecentPlayedRoms.IsMediaGroup AS MostRecentRomIsMediaGroup, User_GameFavouriteRoms.RomId AS FavouriteRomId, User_GameFavouriteRoms.IsMediaGroup AS FavouriteRomIsMediaGroup";
|
||||||
|
UserJoin = @"
|
||||||
|
LEFT JOIN
|
||||||
|
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
|
||||||
|
AND User_RecentPlayedRoms.GameId = RomMediaGroup.GameId
|
||||||
|
AND User_RecentPlayedRoms.PlatformId = RomMediaGroup.PlatformId
|
||||||
|
AND User_RecentPlayedRoms.RomId = RomMediaGroup.Id
|
||||||
|
AND User_RecentPlayedRoms.IsMediaGroup = 1
|
||||||
|
LEFT JOIN
|
||||||
|
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||||
|
AND User_GameFavouriteRoms.GameId = RomMediaGroup.GameId
|
||||||
|
AND User_GameFavouriteRoms.PlatformId = RomMediaGroup.PlatformId
|
||||||
|
AND User_GameFavouriteRoms.RomId = RomMediaGroup.Id
|
||||||
|
AND User_GameFavouriteRoms.IsMediaGroup = 1
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomMediaGroup WHERE GameId=@gameid;";
|
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId" + UserFields + " FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid " + UserJoin + " WHERE RomMediaGroup.GameId=@gameid" + PlatformWhereClause + ";";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("gameid", GameId);
|
{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", userid },
|
||||||
|
{ "platformid", PlatformId }
|
||||||
|
};
|
||||||
|
|
||||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
@@ -91,7 +128,7 @@ namespace gaseous_server.Classes
|
|||||||
mediaGroupItems.Add(BuildMediaGroupFromRow(row));
|
mediaGroupItems.Add(BuildMediaGroupFromRow(row));
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName));
|
mediaGroupItems.Sort((x, y) => x.Platform.CompareTo(y.Platform));
|
||||||
|
|
||||||
return mediaGroupItems;
|
return mediaGroupItems;
|
||||||
}
|
}
|
||||||
@@ -156,7 +193,7 @@ namespace gaseous_server.Classes
|
|||||||
public static void DeleteMediaGroup(long Id)
|
public static void DeleteMediaGroup(long Id)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
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; DELETE FROM User_GameFavouriteRoms WHERE RomId = @id AND IsMediaGroup = 1; DELETE FROM User_RecentPlayedRoms WHERE RomId = @id AND IsMediaGroup = 1; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId=@id AND IsMediaGroup = 1;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", Id);
|
dbDict.Add("id", Id);
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
@@ -170,12 +207,43 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
||||||
{
|
{
|
||||||
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
|
bool hasSaveStates = false;
|
||||||
mediaGroupItem.Id = (long)row["Id"];
|
if (row.Table.Columns.Contains("GameStateRomId"))
|
||||||
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
|
{
|
||||||
mediaGroupItem.PlatformId = (long)row["PlatformId"];
|
if (row["GameStateRomId"] != DBNull.Value)
|
||||||
mediaGroupItem.GameId = (long)row["GameId"];
|
{
|
||||||
mediaGroupItem.RomIds = new List<long>();
|
hasSaveStates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem
|
||||||
|
{
|
||||||
|
Id = (long)row["Id"],
|
||||||
|
Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"],
|
||||||
|
PlatformId = (long)row["PlatformId"],
|
||||||
|
GameId = (long)row["GameId"],
|
||||||
|
RomIds = new List<long>(),
|
||||||
|
Roms = new List<Roms.GameRomItem>(),
|
||||||
|
HasSaveStates = hasSaveStates
|
||||||
|
};
|
||||||
|
|
||||||
|
mediaGroupItem.RomUserLastUsed = false;
|
||||||
|
if (row.Table.Columns.Contains("MostRecentRomId"))
|
||||||
|
{
|
||||||
|
if (row["MostRecentRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
mediaGroupItem.RomUserLastUsed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaGroupItem.RomUserFavourite = false;
|
||||||
|
if (row.Table.Columns.Contains("FavouriteRomId"))
|
||||||
|
{
|
||||||
|
if (row["FavouriteRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
mediaGroupItem.RomUserFavourite = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get members
|
// get members
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
@@ -186,6 +254,14 @@ namespace gaseous_server.Classes
|
|||||||
foreach (DataRow dataRow in data.Rows)
|
foreach (DataRow dataRow in data.Rows)
|
||||||
{
|
{
|
||||||
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]);
|
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mediaGroupItem.Roms.Add(Roms.GetRom((long)dataRow["RomId"]));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Rom Group", "Unable to load ROM data", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mediaGroupItem;
|
return mediaGroupItem;
|
||||||
@@ -220,8 +296,10 @@ namespace gaseous_server.Classes
|
|||||||
GameRomMediaGroupItem mediaGroupItem = GetMediaGroup(Id);
|
GameRomMediaGroupItem mediaGroupItem = GetMediaGroup(Id);
|
||||||
if (mediaGroupItem.Status == GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild)
|
if (mediaGroupItem.Status == GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild)
|
||||||
{
|
{
|
||||||
Game GameObject = Games.GetGame(mediaGroupItem.GameId, false, false, false);
|
MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap(mediaGroupItem.GameId).PreferredMetadataMapItem;
|
||||||
Platform PlatformObject = Platforms.GetPlatform(mediaGroupItem.PlatformId, false);
|
Models.Game GameObject = Games.GetGame(metadataMap.SourceType, metadataMap.SourceId);
|
||||||
|
Platform PlatformObject = Platforms.GetPlatform(mediaGroupItem.PlatformId);
|
||||||
|
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(mediaGroupItem.PlatformId);
|
||||||
|
|
||||||
Logging.Log(Logging.LogType.Information, "Media Group", "Beginning build of media group: " + GameObject.Name + " for platform " + PlatformObject.Name);
|
Logging.Log(Logging.LogType.Information, "Media Group", "Beginning build of media group: " + GameObject.Name + " for platform " + PlatformObject.Name);
|
||||||
|
|
||||||
@@ -256,10 +334,124 @@ namespace gaseous_server.Classes
|
|||||||
foreach (long RomId in mediaGroupItem.RomIds)
|
foreach (long RomId in mediaGroupItem.RomIds)
|
||||||
{
|
{
|
||||||
Roms.GameRomItem rom = Roms.GetRom(RomId);
|
Roms.GameRomItem rom = Roms.GetRom(RomId);
|
||||||
|
bool fileNameFound = false;
|
||||||
if (File.Exists(rom.Path))
|
if (File.Exists(rom.Path))
|
||||||
{
|
{
|
||||||
|
string romExt = Path.GetExtension(rom.Path);
|
||||||
|
if (new string[] { ".zip", ".rar", ".7z" }.Contains(romExt))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Media Group", "Decompressing ROM: " + rom.Name);
|
||||||
|
|
||||||
|
// is compressed
|
||||||
|
switch (romExt)
|
||||||
|
{
|
||||||
|
case ".zip":
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var archive = SharpCompress.Archives.Zip.ZipArchive.Open(rom.Path))
|
||||||
|
{
|
||||||
|
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Media Group", "Extracting file: " + entry.Key);
|
||||||
|
if (fileNameFound == false)
|
||||||
|
{
|
||||||
|
//check if extension is in valid extensions
|
||||||
|
if (platformMapItem.Extensions.SupportedFileExtensions.Contains(Path.GetExtension(entry.Key), StringComparer.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
// update rom file name
|
||||||
|
rom.Name = entry.Key;
|
||||||
|
fileNameFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.WriteToDirectory(ZipFileTempPath, new ExtractionOptions()
|
||||||
|
{
|
||||||
|
ExtractFullPath = true,
|
||||||
|
Overwrite = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception zipEx)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Media Group", "Unzip error", zipEx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ".rar":
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var archive = SharpCompress.Archives.Rar.RarArchive.Open(rom.Path))
|
||||||
|
{
|
||||||
|
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Media Group", "Extracting file: " + entry.Key);
|
||||||
|
if (fileNameFound == false)
|
||||||
|
{
|
||||||
|
//check if extension is in valid extensions
|
||||||
|
if (platformMapItem.Extensions.SupportedFileExtensions.Contains(Path.GetExtension(entry.Key), StringComparer.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
// update rom file name
|
||||||
|
rom.Name = entry.Key;
|
||||||
|
fileNameFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.WriteToDirectory(ZipFileTempPath, new ExtractionOptions()
|
||||||
|
{
|
||||||
|
ExtractFullPath = true,
|
||||||
|
Overwrite = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception zipEx)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Media Group", "Unrar error", zipEx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ".7z":
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(rom.Path))
|
||||||
|
{
|
||||||
|
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Information, "Media Group", "Extracting file: " + entry.Key);
|
||||||
|
if (fileNameFound == false)
|
||||||
|
{
|
||||||
|
//check if extension is in valid extensions
|
||||||
|
if (platformMapItem.Extensions.SupportedFileExtensions.Contains(Path.GetExtension(entry.Key), StringComparer.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
// update rom file name
|
||||||
|
rom.Name = entry.Key;
|
||||||
|
fileNameFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.WriteToDirectory(ZipFileTempPath, new ExtractionOptions()
|
||||||
|
{
|
||||||
|
ExtractFullPath = true,
|
||||||
|
Overwrite = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception zipEx)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Media Group", "7z error", zipEx);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// is uncompressed
|
||||||
Logging.Log(Logging.LogType.Information, "Media Group", "Copying ROM: " + rom.Name);
|
Logging.Log(Logging.LogType.Information, "Media Group", "Copying ROM: " + rom.Name);
|
||||||
File.Copy(rom.Path, Path.Combine(ZipFileTempPath, Path.GetFileName(rom.Path)));
|
File.Copy(rom.Path, Path.Combine(ZipFileTempPath, Path.GetFileName(rom.Path)));
|
||||||
|
}
|
||||||
|
|
||||||
romItems.Add(rom);
|
romItems.Add(rom);
|
||||||
}
|
}
|
||||||
@@ -360,12 +552,13 @@ namespace gaseous_server.Classes
|
|||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public long GameId { get; set; }
|
public long GameId { get; set; }
|
||||||
public long PlatformId { get; set; }
|
public long PlatformId { get; set; }
|
||||||
public string PlatformName {
|
public string Platform
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Platforms.GetPlatform(PlatformId, false).Name;
|
return Platforms.GetPlatform(PlatformId).Name;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -374,8 +567,13 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public List<long> RomIds { get; set; }
|
public List<long> RomIds { get; set; }
|
||||||
|
public List<Roms.GameRomItem> Roms { get; set; }
|
||||||
|
public bool HasSaveStates { get; set; } = false;
|
||||||
|
public bool RomUserLastUsed { get; set; }
|
||||||
|
public bool RomUserFavourite { get; set; }
|
||||||
private GroupBuildStatus _Status { get; set; }
|
private GroupBuildStatus _Status { get; set; }
|
||||||
public GroupBuildStatus Status {
|
public GroupBuildStatus Status
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_Status == GroupBuildStatus.Completed)
|
if (_Status == GroupBuildStatus.Completed)
|
||||||
@@ -399,7 +597,8 @@ namespace gaseous_server.Classes
|
|||||||
_Status = value;
|
_Status = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public long? Size {
|
public long? Size
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Status == GroupBuildStatus.Completed)
|
if (Status == GroupBuildStatus.Completed)
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using System.Data;
|
|||||||
using gaseous_signature_parser.models.RomSignatureObject;
|
using gaseous_signature_parser.models.RomSignatureObject;
|
||||||
using static gaseous_server.Classes.RomMediaGroup;
|
using static gaseous_server.Classes.RomMediaGroup;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using static HasheousClient.Models.FixMatchModel;
|
||||||
|
using NuGet.Protocol.Core.Types;
|
||||||
|
using static gaseous_server.Classes.FileSignature;
|
||||||
|
|
||||||
namespace gaseous_server.Classes
|
namespace gaseous_server.Classes
|
||||||
{
|
{
|
||||||
@@ -11,35 +14,119 @@ namespace gaseous_server.Classes
|
|||||||
public class InvalidRomId : Exception
|
public class InvalidRomId : Exception
|
||||||
{
|
{
|
||||||
public InvalidRomId(long Id) : base("Unable to find ROM by id " + Id)
|
public InvalidRomId(long Id) : base("Unable to find ROM by id " + Id)
|
||||||
{}
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameRomObject GetRoms(long GameId, long PlatformId = -1)
|
public class InvalidRomHash : Exception
|
||||||
|
{
|
||||||
|
public InvalidRomHash(String Hash) : base("Unable to find ROM by hash " + Hash)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0, string userid = "")
|
||||||
{
|
{
|
||||||
GameRomObject GameRoms = new GameRomObject();
|
GameRomObject GameRoms = new GameRomObject();
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "";
|
string sql = "";
|
||||||
|
string sqlCount = "";
|
||||||
|
string sqlPlatform = "";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", GameId);
|
dbDict.Add("id", GameId);
|
||||||
|
dbDict.Add("userid", userid);
|
||||||
|
|
||||||
if (PlatformId == -1) {
|
string NameSearchWhere = "";
|
||||||
sql = "SELECT * FROM Games_Roms WHERE GameId = @id ORDER BY `Name`";
|
if (NameSearch.Length > 0)
|
||||||
} else {
|
{
|
||||||
sql = "SELECT * FROM Games_Roms WHERE GameId = @id AND PlatformId = @platformid ORDER BY `Name`";
|
NameSearchWhere = " AND Games_Roms.`Name` LIKE @namesearch";
|
||||||
dbDict.Add("platformid", PlatformId);
|
dbDict.Add("namesearch", '%' + NameSearch + '%');
|
||||||
}
|
}
|
||||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
|
||||||
|
string UserFields = "";
|
||||||
|
string UserJoin = "";
|
||||||
|
if (userid.Length > 0)
|
||||||
|
{
|
||||||
|
UserFields = ", User_RecentPlayedRoms.RomId AS MostRecentRomId, User_RecentPlayedRoms.IsMediaGroup AS MostRecentRomIsMediaGroup, User_GameFavouriteRoms.RomId AS FavouriteRomId, User_GameFavouriteRoms.IsMediaGroup AS FavouriteRomIsMediaGroup";
|
||||||
|
UserJoin = @"
|
||||||
|
LEFT JOIN
|
||||||
|
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
|
||||||
|
AND User_RecentPlayedRoms.GameId = Games_Roms.MetadataMapId
|
||||||
|
AND User_RecentPlayedRoms.PlatformId = Games_Roms.PlatformId
|
||||||
|
AND User_RecentPlayedRoms.RomId = Games_Roms.Id
|
||||||
|
AND User_RecentPlayedRoms.IsMediaGroup = 0
|
||||||
|
LEFT JOIN
|
||||||
|
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||||
|
AND User_GameFavouriteRoms.GameId = Games_Roms.MetadataMapId
|
||||||
|
AND User_GameFavouriteRoms.PlatformId = Games_Roms.PlatformId
|
||||||
|
AND User_GameFavouriteRoms.RomId = Games_Roms.Id
|
||||||
|
AND User_GameFavouriteRoms.IsMediaGroup = 0
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// data query
|
||||||
|
sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON view_Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (view_Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE view_Games_Roms.MetadataMapId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, view_Games_Roms.`Name`;";
|
||||||
|
|
||||||
|
// count query
|
||||||
|
sqlCount = "SELECT COUNT(view_Games_Roms.Id) AS RomCount FROM view_Games_Roms WHERE view_Games_Roms.MetadataMapId = @id" + NameSearchWhere + ";";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// data query
|
||||||
|
sql = @"
|
||||||
|
SELECT DISTINCT
|
||||||
|
Games_Roms.*,
|
||||||
|
Platform.`Name` AS platformname,
|
||||||
|
view_GamesWithRoms.`Name` AS gamename,
|
||||||
|
GameState.RomId AS SavedStateRomId,
|
||||||
|
CONCAT(`GameLibraries`.`Path`,
|
||||||
|
'/',
|
||||||
|
`Games_Roms`.`RelativePath`) AS `Path`,
|
||||||
|
`GameLibraries`.`Name` AS `LibraryName`
|
||||||
|
" + UserFields + @"
|
||||||
|
FROM
|
||||||
|
Games_Roms
|
||||||
|
JOIN
|
||||||
|
GameLibraries ON Games_Roms.LibraryId = GameLibraries.Id
|
||||||
|
LEFT JOIN
|
||||||
|
Platform ON Games_Roms.PlatformId = Platform.Id AND Platform.SourceId = @platformsource
|
||||||
|
LEFT JOIN
|
||||||
|
view_GamesWithRoms ON view_GamesWithRoms.MetadataMapId = Games_Roms.MetadataMapId
|
||||||
|
LEFT JOIN
|
||||||
|
GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + @"
|
||||||
|
WHERE
|
||||||
|
Games_Roms.MetadataMapId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + @"
|
||||||
|
ORDER BY
|
||||||
|
Platform.`Name`, Games_Roms.`Name`;
|
||||||
|
";
|
||||||
|
|
||||||
|
// count query
|
||||||
|
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.MetadataMapId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||||
|
|
||||||
|
dbDict.Add("platformid", PlatformId);
|
||||||
|
dbDict.Add("platformsource", (int)HasheousClient.Models.MetadataSources.None);
|
||||||
|
}
|
||||||
|
DataTable romDT = db.ExecuteCMD(sql, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks));
|
||||||
|
|
||||||
if (romDT.Rows.Count > 0)
|
if (romDT.Rows.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (DataRow romDR in romDT.Rows)
|
// set count of roms
|
||||||
{
|
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks))[0];
|
||||||
GameRoms.GameRomItems.Add(BuildRom(romDR));
|
GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
|
||||||
}
|
|
||||||
|
|
||||||
// get rom media groups
|
int pageOffset = pageSize * (pageNumber - 1);
|
||||||
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId);
|
for (int i = 0; i < romDT.Rows.Count; i++)
|
||||||
|
{
|
||||||
|
if ((i >= pageOffset && i < pageOffset + pageSize) || pageSize == 0)
|
||||||
|
{
|
||||||
|
GameRomItem gameRomItem = BuildRom(romDT.Rows[i]);
|
||||||
|
GameRoms.GameRomItems.Add(gameRomItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GameRoms;
|
return GameRoms;
|
||||||
}
|
}
|
||||||
@@ -52,7 +139,7 @@ namespace gaseous_server.Classes
|
|||||||
public static GameRomItem GetRom(long RomId)
|
public static GameRomItem GetRom(long RomId)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM Games_Roms WHERE Id = @id";
|
string sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, view_GamesWithRoms.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN view_GamesWithRoms ON view_Games_Roms.MetadataMapId = view_GamesWithRoms.MetadataMapId WHERE view_Games_Roms.Id = @id";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", RomId);
|
dbDict.Add("id", RomId);
|
||||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||||
@@ -69,16 +156,36 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GameRomItem GetRom(string MD5)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT DISTINCT view_Games_Roms.*, Platform.`Name` AS platformname, view_GamesWithRoms.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN view_GamesWithRoms ON view_Games_Roms.MetadataMapId = view_GamesWithRoms.MetadataMapId WHERE view_Games_Roms.MD5 = @id";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
|
dbDict.Add("id", MD5);
|
||||||
|
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (romDT.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
DataRow romDR = romDT.Rows[0];
|
||||||
|
GameRomItem romItem = BuildRom(romDR);
|
||||||
|
return romItem;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidRomHash(MD5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static GameRomItem UpdateRom(long RomId, long PlatformId, long GameId)
|
public static GameRomItem UpdateRom(long RomId, long PlatformId, long GameId)
|
||||||
{
|
{
|
||||||
// ensure metadata for platformid is present
|
// ensure metadata for platformid is present
|
||||||
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
HasheousClient.Models.Metadata.IGDB.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||||
|
|
||||||
// ensure metadata for gameid is present
|
// ensure metadata for gameid is present
|
||||||
IGDB.Models.Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
Models.Game game = Classes.Metadata.Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, GameId);
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id";
|
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, MetadataMapId=@gameid WHERE Id = @id";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", RomId);
|
dbDict.Add("id", RomId);
|
||||||
dbDict.Add("platformid", PlatformId);
|
dbDict.Add("platformid", PlatformId);
|
||||||
@@ -87,6 +194,54 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
GameRomItem rom = GetRom(RomId);
|
GameRomItem rom = GetRom(RomId);
|
||||||
|
|
||||||
|
// send update to Hasheous if enabled
|
||||||
|
if (PlatformId != 0 && GameId != 0)
|
||||||
|
{
|
||||||
|
if (Config.MetadataConfiguration.HasheousSubmitFixes == true)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous &&
|
||||||
|
(
|
||||||
|
Config.MetadataConfiguration.HasheousAPIKey != null &&
|
||||||
|
Config.MetadataConfiguration.HasheousAPIKey != "")
|
||||||
|
)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// find signature used for identifing the rom
|
||||||
|
string md5String = rom.Md5;
|
||||||
|
string sha1String = rom.Sha1;
|
||||||
|
if (rom.Attributes.ContainsKey("ZipContents"))
|
||||||
|
{
|
||||||
|
bool selectorFound = false;
|
||||||
|
List<ArchiveData> archiveDataValues = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ArchiveData>>(rom.Attributes["ZipContents"].ToString());
|
||||||
|
foreach (ArchiveData archiveData in archiveDataValues)
|
||||||
|
{
|
||||||
|
if (archiveData.isSignatureSelector == true)
|
||||||
|
{
|
||||||
|
md5String = archiveData.MD5;
|
||||||
|
sha1String = archiveData.SHA1;
|
||||||
|
selectorFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HasheousClient.WebApp.HttpHelper.APIKey = Config.MetadataConfiguration.HasheousAPIKey;
|
||||||
|
HasheousClient.WebApp.HttpHelper.ClientKey = Config.MetadataConfiguration.HasheousClientAPIKey;
|
||||||
|
HasheousClient.Hasheous hasheousClient = new HasheousClient.Hasheous();
|
||||||
|
List<MetadataMatch> metadataMatchList = new List<MetadataMatch>();
|
||||||
|
metadataMatchList.Add(new MetadataMatch(HasheousClient.Models.MetadataSources.IGDB, platform.Slug, game.Slug));
|
||||||
|
hasheousClient.FixMatch(new HasheousClient.Models.FixMatchModel(md5String, sha1String, metadataMatchList));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Critical, "Fix Match", "An error occurred while sending a fixed match to Hasheous.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return rom;
|
return rom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +256,7 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id";
|
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id; DELETE FROM User_GameFavouriteRoms WHERE RomId = @id AND IsMediaGroup = 0; DELETE FROM User_RecentPlayedRoms WHERE RomId = @id AND IsMediaGroup = 0; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId = @id AND IsMediaGroup = 0;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", RomId);
|
dbDict.Add("id", RomId);
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
@@ -110,38 +265,74 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
private static GameRomItem BuildRom(DataRow romDR)
|
private static GameRomItem BuildRom(DataRow romDR)
|
||||||
{
|
{
|
||||||
|
bool hasSaveStates = false;
|
||||||
|
if (romDR.Table.Columns.Contains("SavedStateRomId"))
|
||||||
|
{
|
||||||
|
if (romDR["SavedStateRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
hasSaveStates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, object> romAttributes = new Dictionary<string, object>();
|
||||||
|
if (romDR["attributes"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ((string)romDR["attributes"] != "[ ]")
|
||||||
|
{
|
||||||
|
romAttributes = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>((string)romDR["attributes"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Roms", "Error parsing rom attributes: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GameRomItem romItem = new GameRomItem
|
GameRomItem romItem = new GameRomItem
|
||||||
{
|
{
|
||||||
Id = (long)romDR["id"],
|
Id = (long)romDR["id"],
|
||||||
PlatformId = (long)romDR["platformid"],
|
PlatformId = (long)romDR["platformid"],
|
||||||
Platform = Classes.Metadata.Platforms.GetPlatform((long)romDR["platformid"]),
|
Platform = (string)romDR["platformname"],
|
||||||
|
MetadataMapId = (long)romDR["metadatamapid"],
|
||||||
|
MetadataSource = (HasheousClient.Models.MetadataSources)(int)romDR["metadatasource"],
|
||||||
GameId = (long)romDR["gameid"],
|
GameId = (long)romDR["gameid"],
|
||||||
|
Game = (string)Common.ReturnValueIfNull(romDR["gamename"], ""),
|
||||||
Name = (string)romDR["name"],
|
Name = (string)romDR["name"],
|
||||||
Size = (long)romDR["size"],
|
Size = (long)romDR["size"],
|
||||||
CRC = ((string)romDR["crc"]).ToLower(),
|
Crc = ((string)romDR["crc"]).ToLower(),
|
||||||
MD5 = ((string)romDR["md5"]).ToLower(),
|
Md5 = ((string)romDR["md5"]).ToLower(),
|
||||||
SHA1 = ((string)romDR["sha1"]).ToLower(),
|
Sha1 = ((string)romDR["sha1"]).ToLower(),
|
||||||
DevelopmentStatus = (string)romDR["developmentstatus"],
|
DevelopmentStatus = (string)romDR["developmentstatus"],
|
||||||
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
|
Attributes = romAttributes,
|
||||||
RomType = (int)romDR["romtype"],
|
RomType = (HasheousClient.Models.SignatureModel.RomItem.RomTypes)(int)romDR["romtype"],
|
||||||
RomTypeMedia = (string)romDR["romtypemedia"],
|
RomTypeMedia = (string)romDR["romtypemedia"],
|
||||||
MediaLabel = (string)romDR["medialabel"],
|
MediaLabel = (string)romDR["medialabel"],
|
||||||
Path = (string)romDR["path"],
|
Path = (string)romDR["path"],
|
||||||
Source = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)romDR["metadatasource"],
|
RelativePath = (string)romDR["relativepath"],
|
||||||
|
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
|
||||||
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
||||||
|
HasSaveStates = hasSaveStates,
|
||||||
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
||||||
};
|
};
|
||||||
|
|
||||||
// check for a web emulator and update the romItem
|
romItem.RomUserLastUsed = false;
|
||||||
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
if (romDR.Table.Columns.Contains("MostRecentRomId"))
|
||||||
{
|
{
|
||||||
if (platformMapping.IGDBId == romItem.PlatformId)
|
if (romDR["MostRecentRomId"] != DBNull.Value)
|
||||||
{
|
{
|
||||||
if (platformMapping.WebEmulator != null)
|
romItem.RomUserLastUsed = true;
|
||||||
{
|
|
||||||
romItem.Emulator = platformMapping.WebEmulator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
romItem.RomUserFavourite = false;
|
||||||
|
if (romDR.Table.Columns.Contains("FavouriteRomId"))
|
||||||
|
{
|
||||||
|
if (romDR["FavouriteRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
romItem.RomUserFavourite = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return romItem;
|
return romItem;
|
||||||
@@ -149,133 +340,25 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
public class GameRomObject
|
public class GameRomObject
|
||||||
{
|
{
|
||||||
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
|
|
||||||
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
|
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
|
||||||
|
public int Count { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GameRomItem
|
public class GameRomItem : HasheousClient.Models.SignatureModel.RomItem
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
|
||||||
public long PlatformId { get; set; }
|
public long PlatformId { get; set; }
|
||||||
public IGDB.Models.Platform Platform { get; set; }
|
public string Platform { get; set; }
|
||||||
//public Dictionary<string, object>? Emulator { get; set; }
|
public long MetadataMapId { get; set; }
|
||||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
public HasheousClient.Models.MetadataSources MetadataSource { get; set; }
|
||||||
public long GameId { get; set; }
|
public long GameId { get; set; }
|
||||||
public string? Name { get; set; }
|
public string Game { get; set; }
|
||||||
public long Size { get; set; }
|
|
||||||
public string? CRC { get; set; }
|
|
||||||
public string? MD5 { get; set; }
|
|
||||||
public string? SHA1 { get; set; }
|
|
||||||
public string? DevelopmentStatus { get; set; }
|
|
||||||
public string[]? Flags { get; set; }
|
|
||||||
public List<KeyValuePair<string, object>>? Attributes { get; set;}
|
|
||||||
public int RomType { get; set; }
|
|
||||||
public string? RomTypeMedia { get; set; }
|
|
||||||
public MediaType? MediaDetail {
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (RomTypeMedia != null)
|
|
||||||
{
|
|
||||||
return new MediaType(Source, RomTypeMedia);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string? MediaLabel { get; set; }
|
|
||||||
public string? Path { get; set; }
|
public string? Path { get; set; }
|
||||||
public RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
|
public string? RelativePath { get; set; }
|
||||||
public string? SignatureSourceGameTitle { get; set;}
|
public string? SignatureSourceGameTitle { get; set; }
|
||||||
|
public bool HasSaveStates { get; set; } = false;
|
||||||
public GameLibrary.LibraryItem Library { get; set; }
|
public GameLibrary.LibraryItem Library { get; set; }
|
||||||
}
|
public bool RomUserLastUsed { get; set; }
|
||||||
|
public bool RomUserFavourite { 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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,21 +8,49 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
{
|
{
|
||||||
public class XMLIngestor : QueueItemStatus
|
public class XMLIngestor : QueueItemStatus
|
||||||
{
|
{
|
||||||
public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType)
|
public void Import(string SearchPath, string ProcessedDirectory, gaseous_signature_parser.parser.SignatureParser XMLType)
|
||||||
{
|
{
|
||||||
// connect to database
|
// connect to database
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
|
string? XMLDBSearchPath = null;
|
||||||
|
string? XMLDBProcessedDirectory = null;
|
||||||
|
if (XMLType == gaseous_signature_parser.parser.SignatureParser.NoIntro)
|
||||||
|
{
|
||||||
|
XMLDBSearchPath = Path.Combine(SearchPath, "DB");
|
||||||
|
XMLDBProcessedDirectory = Path.Combine(ProcessedDirectory, "DB");
|
||||||
|
SearchPath = Path.Combine(SearchPath, "DAT");
|
||||||
|
ProcessedDirectory = Path.Combine(ProcessedDirectory, "DAT");
|
||||||
|
}
|
||||||
|
|
||||||
// process provided files
|
// process provided files
|
||||||
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing from " + SearchPath);
|
|
||||||
if (!Directory.Exists(SearchPath))
|
if (!Directory.Exists(SearchPath))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(SearchPath);
|
Directory.CreateDirectory(SearchPath);
|
||||||
}
|
}
|
||||||
|
if (!Directory.Exists(ProcessedDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(ProcessedDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
string[] PathContents = Directory.GetFiles(SearchPath);
|
string[] PathContents = Directory.GetFiles(SearchPath);
|
||||||
Array.Sort(PathContents);
|
Array.Sort(PathContents);
|
||||||
|
|
||||||
|
string[]? DBPathContents = null;
|
||||||
|
if (XMLDBSearchPath != null)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(XMLDBSearchPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(XMLDBSearchPath);
|
||||||
|
}
|
||||||
|
if (!Directory.Exists(XMLDBProcessedDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(XMLDBProcessedDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBPathContents = Directory.GetFiles(XMLDBSearchPath);
|
||||||
|
}
|
||||||
|
|
||||||
string sql = "";
|
string sql = "";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
System.Data.DataTable sigDB;
|
System.Data.DataTable sigDB;
|
||||||
@@ -33,12 +61,28 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
|
|
||||||
SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile);
|
SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile);
|
||||||
|
|
||||||
if (Common.SkippableFiles.Contains(Path.GetFileName(XMLFile), StringComparer.OrdinalIgnoreCase))
|
Logging.Log(Logging.LogType.Information, "Signature Ingest", "(" + (i + 1) + " / " + PathContents.Length + ") Processing " + XMLType.ToString() + " DAT file: " + XMLFile);
|
||||||
|
|
||||||
|
string? DBFile = null;
|
||||||
|
if (XMLDBSearchPath != null)
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Skipping file: " + XMLFile);
|
switch (XMLType)
|
||||||
|
{
|
||||||
|
case gaseous_signature_parser.parser.SignatureParser.NoIntro:
|
||||||
|
for (UInt16 x = 0; x < DBPathContents.Length; x++)
|
||||||
|
{
|
||||||
|
string tempDBFileName = Path.GetFileNameWithoutExtension(DBPathContents[x].Replace(" (DB Export)", ""));
|
||||||
|
if (tempDBFileName == Path.GetFileNameWithoutExtension(XMLFile))
|
||||||
|
{
|
||||||
|
DBFile = DBPathContents[x];
|
||||||
|
Logging.Log(Logging.LogType.Information, "Signature Ingest", "Using DB file: " + DBFile);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check xml file md5
|
// check xml file md5
|
||||||
Common.hashObject hashObject = new Common.hashObject(XMLFile);
|
Common.hashObject hashObject = new Common.hashObject(XMLFile);
|
||||||
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
|
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
|
||||||
@@ -50,11 +94,9 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing file: " + XMLFile);
|
|
||||||
|
|
||||||
// start parsing file
|
// start parsing file
|
||||||
gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
|
gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
|
||||||
RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, XMLType);
|
RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, DBFile, XMLType);
|
||||||
|
|
||||||
// store in database
|
// store in database
|
||||||
string[] flipNameAndDescription = {
|
string[] flipNameAndDescription = {
|
||||||
@@ -66,21 +108,27 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
bool processGames = false;
|
bool processGames = false;
|
||||||
if (Object.SourceMd5 != null)
|
if (Object.SourceMd5 != null)
|
||||||
{
|
{
|
||||||
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
|
int sourceId = 0;
|
||||||
dbDict = new Dictionary<string, object>();
|
|
||||||
string sourceUriStr = "";
|
sql = "SELECT * FROM Signatures_Sources WHERE `SourceMD5`=@sourcemd5";
|
||||||
if (Object.Url != null)
|
dbDict = new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
sourceUriStr = Object.Url.ToString();
|
{ "name", Common.ReturnValueIfNull(Object.Name, "") },
|
||||||
|
{ "description", Common.ReturnValueIfNull(Object.Description, "") },
|
||||||
|
{ "category", Common.ReturnValueIfNull(Object.Category, "") },
|
||||||
|
{ "version", Common.ReturnValueIfNull(Object.Version, "") },
|
||||||
|
{ "author", Common.ReturnValueIfNull(Object.Author, "") },
|
||||||
|
{ "email", Common.ReturnValueIfNull(Object.Email, "") },
|
||||||
|
{ "homepage", Common.ReturnValueIfNull(Object.Homepage, "") }
|
||||||
|
};
|
||||||
|
if (Object.Url == null)
|
||||||
|
{
|
||||||
|
dbDict.Add("uri", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbDict.Add("uri", Common.ReturnValueIfNull(Object.Url.ToString(), ""));
|
||||||
}
|
}
|
||||||
dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, ""));
|
|
||||||
dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, ""));
|
|
||||||
dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, ""));
|
|
||||||
dbDict.Add("version", Common.ReturnValueIfNull(Object.Version, ""));
|
|
||||||
dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, ""));
|
|
||||||
dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, ""));
|
|
||||||
dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, ""));
|
|
||||||
dbDict.Add("uri", sourceUriStr);
|
|
||||||
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
|
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
|
||||||
dbDict.Add("sourcemd5", Object.SourceMd5);
|
dbDict.Add("sourcemd5", Object.SourceMd5);
|
||||||
dbDict.Add("sourcesha1", Object.SourceSHA1);
|
dbDict.Add("sourcesha1", Object.SourceSHA1);
|
||||||
@@ -89,9 +137,11 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
if (sigDB.Rows.Count == 0)
|
if (sigDB.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// entry not present, insert it
|
// entry not present, insert it
|
||||||
sql = "INSERT INTO Signatures_Sources (Name, Description, Category, Version, Author, Email, Homepage, Url, SourceType, SourceMD5, SourceSHA1) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1)";
|
sql = "INSERT INTO Signatures_Sources (`Name`, `Description`, `Category`, `Version`, `Author`, `Email`, `Homepage`, `Url`, `SourceType`, `SourceMD5`, `SourceSHA1`) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
|
|
||||||
db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
sourceId = Convert.ToInt32(sigDB.Rows[0][0]);
|
||||||
|
|
||||||
processGames = true;
|
processGames = true;
|
||||||
}
|
}
|
||||||
@@ -121,21 +171,88 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
|
dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
|
||||||
dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
|
dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
|
||||||
dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
|
dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
|
||||||
dbDict.Add("country", Common.ReturnValueIfNull(gameObject.Country, ""));
|
|
||||||
dbDict.Add("language", Common.ReturnValueIfNull(gameObject.Language, ""));
|
List<int> gameCountries = new List<int>();
|
||||||
|
if (
|
||||||
|
gameObject.Country != null &&
|
||||||
|
gameObject.Country != "Unknown"
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string[] countries = gameObject.Country.Split(",");
|
||||||
|
foreach (string country in countries)
|
||||||
|
{
|
||||||
|
int countryId = -1;
|
||||||
|
countryId = Common.GetLookupByCode(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
|
||||||
|
if (countryId == -1)
|
||||||
|
{
|
||||||
|
countryId = Common.GetLookupByValue(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
|
||||||
|
|
||||||
|
if (countryId == -1)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Unable to locate country id for " + country.Trim());
|
||||||
|
sql = "INSERT INTO Country (`Code`, `Value`) VALUES (@code, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
|
Dictionary<string, object> countryDict = new Dictionary<string, object>{
|
||||||
|
{ "code", country.Trim() },
|
||||||
|
{ "name", country.Trim() }
|
||||||
|
};
|
||||||
|
countryId = int.Parse(db.ExecuteCMD(sql, countryDict).Rows[0][0].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countryId > 0)
|
||||||
|
{
|
||||||
|
gameCountries.Add(countryId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> gameLanguages = new List<int>();
|
||||||
|
if (
|
||||||
|
gameObject.Language != null &&
|
||||||
|
gameObject.Language != "nolang"
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string[] languages = gameObject.Language.Split(",");
|
||||||
|
foreach (string language in languages)
|
||||||
|
{
|
||||||
|
int languageId = -1;
|
||||||
|
languageId = Common.GetLookupByCode(Common.LookupTypes.Language, (string)Common.ReturnValueIfNull(language.Trim(), ""));
|
||||||
|
if (languageId == -1)
|
||||||
|
{
|
||||||
|
languageId = Common.GetLookupByValue(Common.LookupTypes.Language, (string)Common.ReturnValueIfNull(language.Trim(), ""));
|
||||||
|
|
||||||
|
if (languageId == -1)
|
||||||
|
{
|
||||||
|
Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Unable to locate language id for " + language.Trim());
|
||||||
|
sql = "INSERT INTO Language (`Code`, `Value`) VALUES (@code, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
|
Dictionary<string, object> langDict = new Dictionary<string, object>{
|
||||||
|
{ "code", language.Trim() },
|
||||||
|
{ "name", language.Trim() }
|
||||||
|
};
|
||||||
|
languageId = int.Parse(db.ExecuteCMD(sql, langDict).Rows[0][0].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languageId > 0)
|
||||||
|
{
|
||||||
|
gameLanguages.Add(languageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
|
dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
|
||||||
|
|
||||||
// store platform
|
// store platform
|
||||||
int gameSystem = 0;
|
int gameSystem = 0;
|
||||||
if (gameObject.System != null)
|
if (gameObject.System != null)
|
||||||
{
|
{
|
||||||
sql = "SELECT Id FROM Signatures_Platforms WHERE Platform=@platform";
|
sql = "SELECT `Id` FROM Signatures_Platforms WHERE `Platform`=@platform";
|
||||||
|
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
if (sigDB.Rows.Count == 0)
|
if (sigDB.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// entry not present, insert it
|
// entry not present, insert it
|
||||||
sql = "INSERT INTO Signatures_Platforms (Platform) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
sql = "INSERT INTO Signatures_Platforms (`Platform`) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
|
gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
|
||||||
@@ -151,13 +268,13 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
int gamePublisher = 0;
|
int gamePublisher = 0;
|
||||||
if (gameObject.Publisher != null)
|
if (gameObject.Publisher != null)
|
||||||
{
|
{
|
||||||
sql = "SELECT * FROM Signatures_Publishers WHERE Publisher=@publisher";
|
sql = "SELECT * FROM Signatures_Publishers WHERE `Publisher`=@publisher";
|
||||||
|
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
if (sigDB.Rows.Count == 0)
|
if (sigDB.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// entry not present, insert it
|
// entry not present, insert it
|
||||||
sql = "INSERT INTO Signatures_Publishers (Publisher) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
sql = "INSERT INTO Signatures_Publishers (`Publisher`) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
|
gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
|
||||||
}
|
}
|
||||||
@@ -169,16 +286,16 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
dbDict.Add("publisherid", gamePublisher);
|
dbDict.Add("publisherid", gamePublisher);
|
||||||
|
|
||||||
// store game
|
// store game
|
||||||
int gameId = 0;
|
long gameId = 0;
|
||||||
sql = "SELECT * FROM Signatures_Games WHERE Name=@name AND Year=@year AND Publisherid=@publisher AND Systemid=@systemid AND Country=@country AND Language=@language";
|
sql = "SELECT * FROM Signatures_Games WHERE `Name`=@name AND `Year`=@year AND `PublisherId`=@publisherid AND `SystemId`=@systemid";
|
||||||
|
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
if (sigDB.Rows.Count == 0)
|
if (sigDB.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// entry not present, insert it
|
// entry not present, insert it
|
||||||
sql = "INSERT INTO Signatures_Games " +
|
sql = "INSERT INTO Signatures_Games " +
|
||||||
"(Name, Description, Year, PublisherId, Demo, SystemId, SystemVariant, Video, Country, Language, Copyright) VALUES " +
|
"(`Name`, `Description`, `Year`, `PublisherId`, `Demo`, `SystemId`, `SystemVariant`, `Video`, `Copyright`) VALUES " +
|
||||||
"(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @country, @language, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
"(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
gameId = Convert.ToInt32(sigDB.Rows[0][0]);
|
gameId = Convert.ToInt32(sigDB.Rows[0][0]);
|
||||||
@@ -188,13 +305,57 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
gameId = (int)sigDB.Rows[0][0];
|
gameId = (int)sigDB.Rows[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert countries
|
||||||
|
foreach (int gameCountry in gameCountries)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sql = "SELECT * FROM Signatures_Games_Countries WHERE GameId = @gameid AND CountryId = @Countryid";
|
||||||
|
Dictionary<string, object> countryDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", gameId },
|
||||||
|
{ "Countryid", gameCountry }
|
||||||
|
};
|
||||||
|
if (db.ExecuteCMD(sql, countryDict).Rows.Count == 0)
|
||||||
|
{
|
||||||
|
sql = "INSERT INTO Signatures_Games_Countries (GameId, CountryId) VALUES (@gameid, @Countryid)";
|
||||||
|
db.ExecuteCMD(sql, countryDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("Game id: " + gameId + " with Country " + gameCountry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert languages
|
||||||
|
foreach (int gameLanguage in gameLanguages)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sql = "SELECT * FROM Signatures_Games_Languages WHERE GameId = @gameid AND LanguageId = @languageid";
|
||||||
|
Dictionary<string, object> langDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", gameId },
|
||||||
|
{ "languageid", gameLanguage }
|
||||||
|
};
|
||||||
|
if (db.ExecuteCMD(sql, langDict).Rows.Count == 0)
|
||||||
|
{
|
||||||
|
sql = "INSERT INTO Signatures_Games_Languages (GameId, LanguageId) VALUES (@gameid, @languageid)";
|
||||||
|
db.ExecuteCMD(sql, langDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("Game id: " + gameId + " with language " + gameLanguage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// store rom
|
// store rom
|
||||||
foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
|
foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
|
||||||
{
|
{
|
||||||
if (romObject.Md5 != null || romObject.Sha1 != null)
|
if (romObject.Md5 != null || romObject.Sha1 != null)
|
||||||
{
|
{
|
||||||
int romId = 0;
|
long romId = 0;
|
||||||
sql = "SELECT * FROM Signatures_Roms WHERE GameId=@gameid AND MD5=@md5";
|
sql = "SELECT * FROM Signatures_Roms WHERE `GameId`=@gameid AND (`MD5`=@md5 OR `SHA1`=@sha1)";
|
||||||
dbDict = new Dictionary<string, object>();
|
dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("gameid", gameId);
|
dbDict.Add("gameid", gameId);
|
||||||
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
|
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
|
||||||
@@ -212,12 +373,12 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dbDict.Add("attributes", "[ ]");
|
dbDict.Add("attributes", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dbDict.Add("attributes", "[ ]");
|
dbDict.Add("attributes", "");
|
||||||
}
|
}
|
||||||
dbDict.Add("romtype", (int)romObject.RomType);
|
dbDict.Add("romtype", (int)romObject.RomType);
|
||||||
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
|
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
|
||||||
@@ -229,30 +390,51 @@ namespace gaseous_server.SignatureIngestors.XML
|
|||||||
if (sigDB.Rows.Count == 0)
|
if (sigDB.Rows.Count == 0)
|
||||||
{
|
{
|
||||||
// entry not present, insert it
|
// entry not present, insert it
|
||||||
sql = "INSERT INTO Signatures_Roms (GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, MetadataSource, IngestorVersion) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
sql = "INSERT INTO Signatures_Roms (`GameId`, `Name`, `Size`, `CRC`, `MD5`, `SHA1`, `DevelopmentStatus`, `Attributes`, `RomType`, `RomTypeMedia`, `MediaLabel`, `MetadataSource`, `IngestorVersion`) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||||
sigDB = db.ExecuteCMD(sql, dbDict);
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
|
||||||
romId = Convert.ToInt32(sigDB.Rows[0][0]);
|
romId = Convert.ToInt32(sigDB.Rows[0][0]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
romId = (int)sigDB.Rows[0][0];
|
romId = (int)sigDB.Rows[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// map the rom to the source
|
||||||
|
sql = "SELECT * FROM Signatures_RomToSource WHERE SourceId=@sourceid AND RomId=@romid;";
|
||||||
|
dbDict.Add("romid", romId);
|
||||||
|
dbDict.Add("sourceId", sourceId);
|
||||||
|
|
||||||
|
sigDB = db.ExecuteCMD(sql, dbDict);
|
||||||
|
if (sigDB.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
sql = "INSERT INTO Signatures_RomToSource (`SourceId`, `RomId`) VALUES (@sourceid, @romid);";
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File.Move(XMLFile, Path.Combine(ProcessedDirectory, Path.GetFileName(XMLFile)));
|
||||||
|
if (DBFile != null)
|
||||||
|
{
|
||||||
|
File.Move(DBFile, Path.Combine(XMLDBProcessedDirectory, Path.GetFileName(DBFile)));
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Warning, "Signature Ingestor - XML", "Invalid import file: " + XMLFile, ex);
|
Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Error ingesting " + XMLType.ToString() + " file: " + XMLFile, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Log(Logging.LogType.Debug, "Signature Ingestor - XML", "Rejecting already imported file: " + XMLFile);
|
Logging.Log(Logging.LogType.Information, "Signature Ingest", "Rejecting already imported " + XMLType.ToString() + " file: " + XMLFile);
|
||||||
|
File.Move(XMLFile, Path.Combine(ProcessedDirectory, Path.GetFileName(XMLFile)));
|
||||||
|
if (DBFile != null)
|
||||||
|
{
|
||||||
|
File.Move(DBFile, Path.Combine(XMLDBProcessedDirectory, Path.GetFileName(DBFile)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
157
gaseous-server/Classes/SignatureManagement.cs
Normal file
157
gaseous-server/Classes/SignatureManagement.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using System.Data;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using gaseous_signature_parser.models.RomSignatureObject;
|
||||||
|
using static gaseous_server.Classes.Common;
|
||||||
|
|
||||||
|
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 = (long)(int)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"],
|
||||||
|
Countries = new Dictionary<string, string>(GetLookup(LookupTypes.Country, (long)(int)sigDbRow["Id"])),
|
||||||
|
Languages = new Dictionary<string, string>(GetLookup(LookupTypes.Language, (long)(int)sigDbRow["Id"])),
|
||||||
|
Copyright = (string)sigDbRow["Copyright"]
|
||||||
|
},
|
||||||
|
Rom = new gaseous_server.Models.Signatures_Games.RomItem
|
||||||
|
{
|
||||||
|
Id = (long)(int)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<Dictionary<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Signatures_Sources> GetSources()
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT * FROM Signatures_Sources ORDER BY `SourceType`, `Name`;";
|
||||||
|
DataTable sigDb = db.ExecuteCMD(sql);
|
||||||
|
|
||||||
|
List<Signatures_Sources> SourcesList = new List<Signatures_Sources>();
|
||||||
|
|
||||||
|
foreach (DataRow sigDbRow in sigDb.Rows)
|
||||||
|
{
|
||||||
|
Signatures_Sources sourceItem = new Signatures_Sources
|
||||||
|
{
|
||||||
|
Id = (int)sigDbRow["Id"],
|
||||||
|
Name = (string)sigDbRow["Name"],
|
||||||
|
Description = (string)sigDbRow["Description"],
|
||||||
|
URL = (string)sigDbRow["URL"],
|
||||||
|
Category = (string)sigDbRow["Category"],
|
||||||
|
Version = (string)sigDbRow["Version"],
|
||||||
|
Author = (string)sigDbRow["Author"],
|
||||||
|
Email = (string)sigDbRow["Email"],
|
||||||
|
Homepage = (string)sigDbRow["Homepage"],
|
||||||
|
SourceType = (gaseous_signature_parser.parser.SignatureParser)Enum.Parse(typeof(gaseous_signature_parser.parser.SignatureParser), sigDbRow["SourceType"].ToString()),
|
||||||
|
MD5 = (string)sigDbRow["SourceMD5"],
|
||||||
|
SHA1 = (string)sigDbRow["SourceSHA1"]
|
||||||
|
};
|
||||||
|
SourcesList.Add(sourceItem);
|
||||||
|
}
|
||||||
|
return SourcesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSource(int sourceId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "DELETE FROM Signatures_Sources WHERE Id = @sourceId;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "sourceId", sourceId }
|
||||||
|
};
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, string> GetLookup(LookupTypes LookupType, long GameId)
|
||||||
|
{
|
||||||
|
string tableName = "";
|
||||||
|
switch (LookupType)
|
||||||
|
{
|
||||||
|
case LookupTypes.Country:
|
||||||
|
tableName = "Countries";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LookupTypes.Language:
|
||||||
|
tableName = "Languages";
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT " + LookupType.ToString() + ".Code, " + LookupType.ToString() + ".Value FROM Signatures_Games_" + tableName + " JOIN " + LookupType.ToString() + " ON Signatures_Games_" + tableName + "." + LookupType.ToString() + "Id = " + LookupType.ToString() + ".Id WHERE Signatures_Games_" + tableName + ".GameId = @id;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "id", GameId }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
Dictionary<string, string> returnDict = new Dictionary<string, string>();
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
returnDict.Add((string)row["Code"], (string)row["Value"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnDict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
gaseous-server/Classes/Statistics.cs
Normal file
117
gaseous-server/Classes/Statistics.cs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
using System.Data;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
|
||||||
|
namespace gaseous_server.Classes
|
||||||
|
{
|
||||||
|
public class Statistics
|
||||||
|
{
|
||||||
|
public StatisticsModel RecordSession(Guid SessionId, long GameId, long PlatformId, long RomId, bool IsMediaGroup, string UserId)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql;
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "platformid", PlatformId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
|
||||||
|
// update last played rom id
|
||||||
|
sql = "INSERT INTO User_RecentPlayedRoms (UserId, GameId, PlatformId, RomId, IsMediaGroup) VALUES (@userid, @gameid, @platformid, @romid, @ismediagroup) ON DUPLICATE KEY UPDATE RomId = @romid, IsMediaGroup = @ismediagroup;";
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
|
||||||
|
// update sessions
|
||||||
|
|
||||||
|
if (SessionId == Guid.Empty)
|
||||||
|
{
|
||||||
|
// new session required
|
||||||
|
SessionId = Guid.NewGuid();
|
||||||
|
|
||||||
|
sql = "INSERT INTO UserTimeTracking (GameId, UserId, SessionId, SessionTime, SessionLength, PlatformId, IsMediaGroup, RomId) VALUES (@gameid, @userid, @sessionid, @sessiontime, @sessionlength, @platformid, @ismediagroup, @romid);";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "sessionid", SessionId },
|
||||||
|
{ "sessiontime", DateTime.UtcNow },
|
||||||
|
{ "sessionlength", 1 },
|
||||||
|
{ "platformid", PlatformId },
|
||||||
|
{ "ismediagroup", IsMediaGroup },
|
||||||
|
{ "romid", RomId }
|
||||||
|
};
|
||||||
|
|
||||||
|
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"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
218
gaseous-server/Classes/UserProfile.cs
Normal file
218
gaseous-server/Classes/UserProfile.cs
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
using System.Data;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using HasheousClient.Models;
|
||||||
|
|
||||||
|
namespace gaseous_server.Classes
|
||||||
|
{
|
||||||
|
public class UserProfile
|
||||||
|
{
|
||||||
|
static readonly Dictionary<string, string> supportedImages = new Dictionary<string, string>{
|
||||||
|
{ ".png", "image/png" },
|
||||||
|
{ ".jpg", "image/jpeg" },
|
||||||
|
{ ".jpeg", "image/jpeg" },
|
||||||
|
{ ".gif", "image/gif" },
|
||||||
|
{ ".bmp", "image/bmp" },
|
||||||
|
{ ".svg", "image/svg+xml" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public Models.UserProfile? GetUserProfile(string UserId)
|
||||||
|
{
|
||||||
|
// build the user profile object
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Id, UserId, DisplayName, Quip, AvatarExtension, ProfileBackgroundExtension, UnstructuredData FROM UserProfiles WHERE Id = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "userid", UserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Models.UserProfile.ProfileImageItem? Avatar = null;
|
||||||
|
if (data.Rows[0]["AvatarExtension"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
Avatar = new Models.UserProfile.ProfileImageItem
|
||||||
|
{
|
||||||
|
MimeType = supportedImages[data.Rows[0]["AvatarExtension"].ToString()],
|
||||||
|
Extension = data.Rows[0]["AvatarExtension"].ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Models.UserProfile.ProfileImageItem? ProfileBackground = null;
|
||||||
|
if (data.Rows[0]["ProfileBackgroundExtension"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
ProfileBackground = new Models.UserProfile.ProfileImageItem
|
||||||
|
{
|
||||||
|
MimeType = supportedImages[data.Rows[0]["ProfileBackgroundExtension"].ToString()],
|
||||||
|
Extension = data.Rows[0]["ProfileBackgroundExtension"].ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// get now playing game - if available
|
||||||
|
Models.UserProfile.NowPlayingItem? NowPlaying = null;
|
||||||
|
sql = "SELECT * FROM `view_UserTimeTracking` WHERE UserId = @userid AND UTC_TIMESTAMP() BETWEEN SessionTime AND DATE_ADD(SessionEnd, INTERVAL 2 MINUTE) ORDER BY SessionEnd DESC LIMIT 1;";
|
||||||
|
dbDict = new Dictionary<string, object>{
|
||||||
|
{ "userid", data.Rows[0]["UserId"].ToString() }
|
||||||
|
};
|
||||||
|
DataTable nowPlayingData = db.ExecuteCMD(sql, dbDict);
|
||||||
|
if (nowPlayingData.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gaseous_server.Models.MetadataMap.MetadataMapItem metadataMap = Classes.MetadataManagement.GetMetadataMap((long)nowPlayingData.Rows[0]["GameId"]).PreferredMetadataMapItem;
|
||||||
|
NowPlaying = new Models.UserProfile.NowPlayingItem
|
||||||
|
{
|
||||||
|
Game = Games.GetGame(metadataMap.SourceType, metadataMap.SourceId),
|
||||||
|
Platform = Platforms.GetPlatform((long)nowPlayingData.Rows[0]["PlatformId"]),
|
||||||
|
Duration = Convert.ToInt64(nowPlayingData.Rows[0]["SessionLength"])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the user profile object
|
||||||
|
return new Models.UserProfile
|
||||||
|
{
|
||||||
|
UserId = Guid.Parse(data.Rows[0]["Id"].ToString()),
|
||||||
|
DisplayName = data.Rows[0]["DisplayName"].ToString(),
|
||||||
|
Quip = data.Rows[0]["Quip"].ToString(),
|
||||||
|
Avatar = Avatar,
|
||||||
|
ProfileBackground = ProfileBackground,
|
||||||
|
NowPlaying = NowPlaying,
|
||||||
|
Data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(data.Rows[0]["UnstructuredData"].ToString())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUserProfile(string InternalUserId, Models.UserProfile profile)
|
||||||
|
{
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "UPDATE UserProfiles SET DisplayName = @displayname, Quip = @quip, UnstructuredData = @data WHERE UserId = @internalId AND Id = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "displayname", profile.DisplayName },
|
||||||
|
{ "quip", profile.Quip },
|
||||||
|
{ "data", Newtonsoft.Json.JsonConvert.SerializeObject(profile.Data) },
|
||||||
|
{ "userid", profile.UserId },
|
||||||
|
{ "internalId", InternalUserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ImageType
|
||||||
|
{
|
||||||
|
Avatar,
|
||||||
|
Background
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateImage(ImageType imageType, string UserId, string InternalUserId, string Filename, byte[] bytes)
|
||||||
|
{
|
||||||
|
// check if it's a supported file type
|
||||||
|
if (!supportedImages.ContainsKey(Path.GetExtension(Filename).ToLower()))
|
||||||
|
{
|
||||||
|
throw new Exception("File type not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
string ByteFieldName;
|
||||||
|
string ExtensionFieldName;
|
||||||
|
switch (imageType)
|
||||||
|
{
|
||||||
|
case ImageType.Avatar:
|
||||||
|
ByteFieldName = "Avatar";
|
||||||
|
ExtensionFieldName = "AvatarExtension";
|
||||||
|
break;
|
||||||
|
case ImageType.Background:
|
||||||
|
ByteFieldName = "ProfileBackground";
|
||||||
|
ExtensionFieldName = "ProfileBackgroundExtension";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid image type");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = String.Format("UPDATE UserProfiles SET {0} = @content, {1} = @extension WHERE Id = @userid AND UserId = @internaluserid;", ByteFieldName, ExtensionFieldName);
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "content", bytes },
|
||||||
|
{ "extension", Path.GetExtension(Filename) },
|
||||||
|
{ "userid", UserId },
|
||||||
|
{ "internaluserid", InternalUserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.ImageItem? GetImage(ImageType imageType, string UserId)
|
||||||
|
{
|
||||||
|
string ByteFieldName;
|
||||||
|
string ExtensionFieldName;
|
||||||
|
switch (imageType)
|
||||||
|
{
|
||||||
|
case ImageType.Avatar:
|
||||||
|
ByteFieldName = "Avatar";
|
||||||
|
ExtensionFieldName = "AvatarExtension";
|
||||||
|
break;
|
||||||
|
case ImageType.Background:
|
||||||
|
ByteFieldName = "ProfileBackground";
|
||||||
|
ExtensionFieldName = "ProfileBackgroundExtension";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid image type");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = String.Format("SELECT {0}, {1} FROM UserProfiles WHERE Id = @userid;", ByteFieldName, ExtensionFieldName);
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "userid", UserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Models.ImageItem? image = new Models.ImageItem
|
||||||
|
{
|
||||||
|
content = data.Rows[0][ByteFieldName] as byte[],
|
||||||
|
mimeType = supportedImages[data.Rows[0][ExtensionFieldName] as string],
|
||||||
|
extension = data.Rows[0][ExtensionFieldName] as string
|
||||||
|
};
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteImage(ImageType imageType, string UserId)
|
||||||
|
{
|
||||||
|
string ByteFieldName;
|
||||||
|
string ExtensionFieldName;
|
||||||
|
switch (imageType)
|
||||||
|
{
|
||||||
|
case ImageType.Avatar:
|
||||||
|
ByteFieldName = "Avatar";
|
||||||
|
ExtensionFieldName = "AvatarExtension";
|
||||||
|
break;
|
||||||
|
case ImageType.Background:
|
||||||
|
ByteFieldName = "ProfileBackground";
|
||||||
|
ExtensionFieldName = "ProfileBackgroundExtension";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid image type");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = String.Format("UPDATE UserProfiles SET {0} = NULL, {1} = NULL WHERE UserId = @userid;", ByteFieldName, ExtensionFieldName);
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>{
|
||||||
|
{ "userid", UserId }
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ExecuteCMD(sql, dbDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Asp.Versioning;
|
||||||
|
using IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -97,6 +99,7 @@ namespace gaseous_server.Controllers
|
|||||||
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
|
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
|
||||||
profile.SecurityProfile = user.SecurityProfile;
|
profile.SecurityProfile = user.SecurityProfile;
|
||||||
profile.UserPreferences = user.UserPreferences;
|
profile.UserPreferences = user.UserPreferences;
|
||||||
|
profile.ProfileId = user.ProfileId;
|
||||||
profile.Roles.Sort();
|
profile.Roles.Sort();
|
||||||
|
|
||||||
return Ok(profile);
|
return Ok(profile);
|
||||||
@@ -185,6 +188,7 @@ namespace gaseous_server.Controllers
|
|||||||
user.LockoutEnabled = rawUser.LockoutEnabled;
|
user.LockoutEnabled = rawUser.LockoutEnabled;
|
||||||
user.LockoutEnd = rawUser.LockoutEnd;
|
user.LockoutEnd = rawUser.LockoutEnd;
|
||||||
user.SecurityProfile = rawUser.SecurityProfile;
|
user.SecurityProfile = rawUser.SecurityProfile;
|
||||||
|
user.ProfileId = rawUser.ProfileId;
|
||||||
|
|
||||||
// get roles
|
// get roles
|
||||||
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
|
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
|
||||||
@@ -216,6 +220,10 @@ namespace gaseous_server.Controllers
|
|||||||
Email = model.Email,
|
Email = model.Email,
|
||||||
NormalizedEmail = model.Email.ToUpper()
|
NormalizedEmail = model.Email.ToUpper()
|
||||||
};
|
};
|
||||||
|
if (await _userManager.FindByEmailAsync(model.Email) != null)
|
||||||
|
{
|
||||||
|
return NotFound("User already exists");
|
||||||
|
}
|
||||||
var result = await _userManager.CreateAsync(user, model.Password);
|
var result = await _userManager.CreateAsync(user, model.Password);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
@@ -237,6 +245,23 @@ namespace gaseous_server.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Users/Test")]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public async Task<IActionResult> TestUserExists(string Email)
|
||||||
|
{
|
||||||
|
ApplicationUser? rawUser = await _userManager.FindByEmailAsync(Email);
|
||||||
|
|
||||||
|
if (rawUser != null)
|
||||||
|
{
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Users/{UserId}")]
|
[Route("Users/{UserId}")]
|
||||||
[Authorize(Roles = "Admin")]
|
[Authorize(Roles = "Admin")]
|
||||||
@@ -252,6 +277,7 @@ namespace gaseous_server.Controllers
|
|||||||
user.LockoutEnabled = rawUser.LockoutEnabled;
|
user.LockoutEnabled = rawUser.LockoutEnabled;
|
||||||
user.LockoutEnd = rawUser.LockoutEnd;
|
user.LockoutEnd = rawUser.LockoutEnd;
|
||||||
user.SecurityProfile = rawUser.SecurityProfile;
|
user.SecurityProfile = rawUser.SecurityProfile;
|
||||||
|
user.ProfileId = rawUser.ProfileId;
|
||||||
|
|
||||||
// get roles
|
// get roles
|
||||||
IList<string> aUserRoles = await _userManager.GetRolesAsync(rawUser);
|
IList<string> aUserRoles = await _userManager.GetRolesAsync(rawUser);
|
||||||
@@ -302,7 +328,7 @@ namespace gaseous_server.Controllers
|
|||||||
// delete all roles
|
// delete all roles
|
||||||
foreach (string role in userRoles)
|
foreach (string role in userRoles)
|
||||||
{
|
{
|
||||||
if ((new string[] { "Admin", "Gamer", "Player" }).Contains(role) )
|
if ((new string[] { "Admin", "Gamer", "Player" }).Contains(role))
|
||||||
{
|
{
|
||||||
await _userManager.RemoveFromRoleAsync(user, role);
|
await _userManager.RemoveFromRoleAsync(user, role);
|
||||||
}
|
}
|
||||||
@@ -413,5 +439,115 @@ namespace gaseous_server.Controllers
|
|||||||
return Ok();
|
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 System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ using System.Threading.Tasks;
|
|||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
using Authentication;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using gaseous_server.Classes.Metadata;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -16,6 +22,15 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class BiosController : Controller
|
public class BiosController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
|
public BiosController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -42,24 +57,55 @@ namespace gaseous_server.Controllers
|
|||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpHead]
|
[HttpHead]
|
||||||
[Route("zip/{PlatformId}")]
|
[Route("zip/{PlatformId}")]
|
||||||
|
[Route("zip/{PlatformId}/{GameId}")]
|
||||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult GetBiosCompressed(long PlatformId)
|
public async Task<ActionResult> GetBiosCompressedAsync(long PlatformId, long GameId = -1, bool filtered = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
Platform platform = Platforms.GetPlatform(PlatformId);
|
||||||
|
PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap(PlatformId);
|
||||||
|
|
||||||
string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platform.Slug);
|
List<string> biosHashes = new List<string>();
|
||||||
|
|
||||||
|
if (GameId == -1 || filtered == false)
|
||||||
|
{
|
||||||
|
// get all bios files for selected platform
|
||||||
|
biosHashes.AddRange(platformMap.EnabledBIOSHashes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get user platform map
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
|
PlatformMapping platformMapping = new PlatformMapping();
|
||||||
|
PlatformMapping.PlatformMapItem userPlatformMap = platformMapping.GetUserPlatformMap(user.Id, PlatformId, GameId);
|
||||||
|
|
||||||
|
biosHashes.AddRange(userPlatformMap.EnabledBIOSHashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build zip file
|
||||||
string tempFile = Path.GetTempFileName();
|
string tempFile = Path.GetTempFileName();
|
||||||
|
|
||||||
using (FileStream zipFile = System.IO.File.Create(tempFile))
|
using (FileStream zipFile = System.IO.File.Create(tempFile))
|
||||||
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
|
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
|
||||||
{
|
{
|
||||||
foreach (string file in Directory.GetFiles(biosPath))
|
foreach (string hash in biosHashes)
|
||||||
{
|
{
|
||||||
zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
|
// get the bios data for the hash
|
||||||
|
foreach (PlatformMapping.PlatformMapItem.EmulatorBiosItem bios in platformMap.Bios)
|
||||||
|
{
|
||||||
|
if (bios.hash == hash)
|
||||||
|
{
|
||||||
|
// add the bios file to the zip
|
||||||
|
string biosFilePath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios");
|
||||||
|
if (System.IO.File.Exists(biosFilePath))
|
||||||
|
{
|
||||||
|
zipArchive.CreateEntryFromFile(biosFilePath, bios.filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ using System.Collections.Generic;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Authentication;
|
||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -16,6 +19,17 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class CollectionsController : Controller
|
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>
|
/// <summary>
|
||||||
/// Gets all ROM collections
|
/// Gets all ROM collections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -24,9 +38,16 @@ namespace gaseous_server.Controllers
|
|||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[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>
|
/// <summary>
|
||||||
@@ -41,22 +62,31 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{CollectionId}")]
|
[Route("{CollectionId}")]
|
||||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
if (Build == true)
|
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
|
catch
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the contents of the specified ROM collection
|
/// Gets the contents of the specified ROM collection
|
||||||
@@ -69,18 +99,27 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{CollectionId}/Roms")]
|
[Route("{CollectionId}/Roms")]
|
||||||
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
|
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id);
|
||||||
return Ok(Classes.Collections.GetCollectionContent(collectionItem));
|
return Ok(Classes.Collections.GetCollectionContent(collectionItem, user.Id));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a preview of the provided collection item
|
/// Gets a preview of the provided collection item
|
||||||
@@ -94,17 +133,26 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize(Roles = "Admin,Gamer")]
|
[Authorize(Roles = "Admin,Gamer")]
|
||||||
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
return Ok(Classes.Collections.GetCollectionContent(Item));
|
return Ok(Classes.Collections.GetCollectionContent(Item, user.Id));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return NotFound(ex);
|
return NotFound(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets ROM collection in zip format
|
/// Gets ROM collection in zip format
|
||||||
@@ -117,11 +165,15 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{CollectionId}/Roms/Zip")]
|
[Route("{CollectionId}/Roms/Zip")]
|
||||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
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");
|
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip");
|
||||||
|
|
||||||
@@ -140,6 +192,11 @@ namespace gaseous_server.Controllers
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new ROM collection
|
/// Creates a new ROM collection
|
||||||
@@ -152,17 +209,26 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize(Roles = "Admin,Gamer")]
|
[Authorize(Roles = "Admin,Gamer")]
|
||||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
return Ok(Classes.Collections.NewCollection(Item));
|
return Ok(Classes.Collections.NewCollection(Item, user.Id));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return BadRequest(ex);
|
return BadRequest(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Edits an existing collection
|
/// Edits an existing collection
|
||||||
@@ -177,17 +243,26 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize(Roles = "Admin,Gamer")]
|
[Authorize(Roles = "Admin,Gamer")]
|
||||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
return Ok(Classes.Collections.EditCollection(CollectionId, Item, true));
|
return Ok(Classes.Collections.EditCollection(CollectionId, Item, user.Id, true));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Edits an existing collection
|
/// Edits an existing collection
|
||||||
@@ -202,11 +277,15 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{CollectionId}/AlwaysInclude")]
|
[Route("{CollectionId}/AlwaysInclude")]
|
||||||
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
|
Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id);
|
||||||
bool ItemFound = false;
|
bool ItemFound = false;
|
||||||
foreach (Collections.CollectionItem.AlwaysIncludeItem includeItem in collectionItem.AlwaysInclude)
|
foreach (Collections.CollectionItem.AlwaysIncludeItem includeItem in collectionItem.AlwaysInclude)
|
||||||
{
|
{
|
||||||
@@ -220,13 +299,18 @@ namespace gaseous_server.Controllers
|
|||||||
collectionItem.AlwaysInclude.Add(Inclusion);
|
collectionItem.AlwaysInclude.Add(Inclusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, Rebuild));
|
return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, user.Id, Rebuild));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the specified ROM collection
|
/// Deletes the specified ROM collection
|
||||||
@@ -239,11 +323,15 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{CollectionId}")]
|
[Route("{CollectionId}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[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
|
try
|
||||||
{
|
{
|
||||||
Classes.Collections.DeleteCollection(CollectionId);
|
Classes.Collections.DeleteCollection(CollectionId, user.Id);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -251,5 +339,10 @@ namespace gaseous_server.Controllers
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -85,5 +86,24 @@ namespace gaseous_server.Controllers
|
|||||||
return NotFound(exLNF.ToString());
|
return NotFound(exLNF.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpPost("{LibraryId}/Scan")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public ActionResult ScanLibrary(int LibraryId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GameLibrary.ScanLibrary(LibraryId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (GameLibrary.LibraryNotFound exLNF)
|
||||||
|
{
|
||||||
|
return NotFound(exLNF.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.CodeAnalysis.Scripting;
|
using Microsoft.CodeAnalysis.Scripting;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Text;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -37,6 +39,32 @@ namespace gaseous_server.Controllers
|
|||||||
return Ok(PlatformMapping.PlatformMap);
|
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.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -121,35 +149,6 @@ namespace gaseous_server.Controllers
|
|||||||
return Ok(new { count = files.Count, size });
|
return Ok(new { count = files.Count, size });
|
||||||
}
|
}
|
||||||
|
|
||||||
// [MapToApiVersion("1.0")]
|
|
||||||
[MapToApiVersion("1.1")]
|
|
||||||
[HttpPost]
|
|
||||||
// [Route("{PlatformId}")]
|
|
||||||
// [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
|
|
||||||
// [ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
// [ProducesResponseType(StatusCodes.Status409Conflict)]
|
|
||||||
// public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId);
|
|
||||||
|
|
||||||
// if (platformMapItem != null)
|
|
||||||
// {
|
|
||||||
// return Conflict();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// PlatformMapping.WritePlatformMap(Map, false, false);
|
|
||||||
// return Ok(PlatformMapping.GetPlatformMap(PlatformId));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch
|
|
||||||
// {
|
|
||||||
// return NotFound();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpPatch]
|
[HttpPatch]
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ using System.Threading.Tasks;
|
|||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using gaseous_server.Models;
|
using gaseous_server.Models;
|
||||||
using IGDB.Models;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.CodeAnalysis.Scripting;
|
using Microsoft.CodeAnalysis.Scripting;
|
||||||
|
using Asp.Versioning;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,7 @@ namespace gaseous_server.Controllers
|
|||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
|
||||||
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM Games_Roms) ORDER BY `Name` ASC;";
|
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM view_Games_Roms) ORDER BY `Name` ASC;";
|
||||||
|
|
||||||
List<Platform> RetVal = new List<Platform>();
|
List<Platform> RetVal = new List<Platform>();
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ namespace gaseous_server.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||||
|
|
||||||
if (platformObject != null)
|
if (platformObject != null)
|
||||||
{
|
{
|
||||||
@@ -76,59 +77,166 @@ namespace gaseous_server.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
// [MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
// [MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
// [HttpGet]
|
||||||
[Route("{PlatformId}/platformlogo")]
|
// [Route("{PlatformId}/platformlogo")]
|
||||||
[ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
|
// [ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
// [ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult PlatformLogo(long PlatformId)
|
// public ActionResult PlatformLogo(long PlatformId)
|
||||||
{
|
// {
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
// Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||||
if (platformObject != null)
|
// if (platformObject != null)
|
||||||
{
|
// {
|
||||||
IGDB.Models.PlatformLogo logoObject = PlatformLogos.GetPlatformLogo(platformObject.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
|
// PlatformLogo logoObjectParent = (PlatformLogo)platformObject.PlatformLogo;
|
||||||
if (logoObject != null)
|
// PlatformLogo logoObject = PlatformLogos.GetPlatformLogo(logoObjectParent.Id);
|
||||||
{
|
// if (logoObject != null)
|
||||||
return Ok(logoObject);
|
// {
|
||||||
}
|
// return Ok(logoObject);
|
||||||
else
|
// }
|
||||||
{
|
// else
|
||||||
return NotFound();
|
// {
|
||||||
}
|
// return NotFound();
|
||||||
}
|
// }
|
||||||
else
|
// }
|
||||||
{
|
// else
|
||||||
return NotFound();
|
// {
|
||||||
}
|
// return NotFound();
|
||||||
}
|
// }
|
||||||
catch
|
// }
|
||||||
{
|
// catch
|
||||||
return NotFound();
|
// {
|
||||||
}
|
// return NotFound();
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("{PlatformId}/platformlogo/image")]
|
[Route("{PlatformId}/platformlogo/{size}/")]
|
||||||
|
[Route("{PlatformId}/platformlogo/{size}/logo.png")]
|
||||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult PlatformLogoImage(long PlatformId)
|
public async Task<ActionResult> GameImage(long PlatformId, Communications.IGDBAPI_ImageSize size)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
HasheousClient.Models.MetadataSources metadataSources = HasheousClient.Models.MetadataSources.None;
|
||||||
|
|
||||||
string logoFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject), "Logo_Medium.png");
|
Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId, metadataSources);
|
||||||
if (System.IO.File.Exists(logoFilePath))
|
PlatformLogo? logoObject = null;
|
||||||
|
|
||||||
|
logoObject = PlatformLogos.GetPlatformLogo((long)platformObject.PlatformLogo, metadataSources);
|
||||||
|
|
||||||
|
if (logoObject == null)
|
||||||
{
|
{
|
||||||
string filename = "Logo.png";
|
// getting the logo failed, so we'll try a platform variant if available
|
||||||
string filepath = logoFilePath;
|
if (platformObject.Versions != null)
|
||||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
{
|
||||||
string contentType = "image/png";
|
if (platformObject.Versions.Count > 0)
|
||||||
|
{
|
||||||
|
PlatformVersion platformVersion = Classes.Metadata.PlatformVersions.GetPlatformVersion(metadataSources, (long)platformObject.Versions[0]);
|
||||||
|
logoObject = PlatformLogos.GetPlatformLogo((long)platformVersion.PlatformLogo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetDummyImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetDummyImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject), metadataSources.ToString());
|
||||||
|
string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(imagePath))
|
||||||
|
{
|
||||||
|
Communications comms = new Communications();
|
||||||
|
Task<string> ImgFetch = comms.GetSpecificImageFromServer(metadataSources, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject)), logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||||
|
|
||||||
|
imagePath = ImgFetch.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(imagePath))
|
||||||
|
{
|
||||||
|
Communications comms = new Communications();
|
||||||
|
Task<string> ImgFetch = comms.GetSpecificImageFromServer(metadataSources, basePath, logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||||
|
|
||||||
|
imagePath = ImgFetch.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(imagePath))
|
||||||
|
{
|
||||||
|
// get image info
|
||||||
|
var info = new ImageMagick.MagickImageInfo(imagePath);
|
||||||
|
string extension = ".jpg";
|
||||||
|
string mimeType = "image/jpg";
|
||||||
|
switch (info.Format)
|
||||||
|
{
|
||||||
|
case ImageMagick.MagickFormat.Jpeg:
|
||||||
|
extension = ".jpg";
|
||||||
|
mimeType = "image/jpg";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Png:
|
||||||
|
extension = ".png";
|
||||||
|
mimeType = "image/png";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Gif:
|
||||||
|
extension = ".gif";
|
||||||
|
mimeType = "image/gif";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Bmp:
|
||||||
|
extension = ".bmp";
|
||||||
|
mimeType = "image/bmp";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Tiff:
|
||||||
|
extension = ".tiff";
|
||||||
|
mimeType = "image/tiff";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Unknown:
|
||||||
|
extension = ".jpg";
|
||||||
|
mimeType = "image/jpg";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.WebP:
|
||||||
|
extension = ".webp";
|
||||||
|
mimeType = "image/webp";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Heic:
|
||||||
|
extension = ".heic";
|
||||||
|
mimeType = "image/heic";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Heif:
|
||||||
|
extension = ".heif";
|
||||||
|
mimeType = "image/heif";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImageMagick.MagickFormat.Svg:
|
||||||
|
extension = ".svg";
|
||||||
|
mimeType = "image/svg+xml";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
extension = ".jpg";
|
||||||
|
mimeType = "image/jpg";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string filename = logoObject.ImageId + extension;
|
||||||
|
string filepath = imagePath;
|
||||||
|
string contentType = mimeType;
|
||||||
|
|
||||||
var cd = new System.Net.Mime.ContentDisposition
|
var cd = new System.Net.Mime.ContentDisposition
|
||||||
{
|
{
|
||||||
@@ -137,6 +245,16 @@ namespace gaseous_server.Controllers
|
|||||||
};
|
};
|
||||||
|
|
||||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||||
|
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||||
|
|
||||||
|
byte[] filedata = null;
|
||||||
|
using (FileStream fs = System.IO.File.OpenRead(filepath))
|
||||||
|
{
|
||||||
|
using (BinaryReader binaryReader = new BinaryReader(fs))
|
||||||
|
{
|
||||||
|
filedata = binaryReader.ReadBytes((int)fs.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return File(filedata, contentType);
|
return File(filedata, contentType);
|
||||||
}
|
}
|
||||||
@@ -150,6 +268,50 @@ namespace gaseous_server.Controllers
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ActionResult GetDummyImage()
|
||||||
|
{
|
||||||
|
// return resource named DefaultPlatformLogo.svg
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
string resourceName = "gaseous_server.Support.DefaultPlatformLogo.svg";
|
||||||
|
string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||||
|
if (resources.Contains(resourceName))
|
||||||
|
{
|
||||||
|
string svgData = "";
|
||||||
|
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
svgData = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cd = new System.Net.Mime.ContentDisposition
|
||||||
|
{
|
||||||
|
FileName = "DefaultPlatformLogo.svg",
|
||||||
|
Inline = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||||
|
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||||
|
|
||||||
|
byte[] filedata = null;
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (StreamWriter writer = new StreamWriter(ms))
|
||||||
|
{
|
||||||
|
writer.Write(svgData);
|
||||||
|
writer.Flush();
|
||||||
|
ms.Position = 0;
|
||||||
|
filedata = ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(filedata, "image/svg+xml");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ using System.Reflection;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using IGDB.Models;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.CodeAnalysis.Scripting;
|
using Microsoft.CodeAnalysis.Scripting;
|
||||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||||
|
using Asp.Versioning;
|
||||||
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -30,54 +31,59 @@ namespace gaseous_server.Controllers
|
|||||||
[Authorize(Roles = "Admin,Gamer")]
|
[Authorize(Roles = "Admin,Gamer")]
|
||||||
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
|
||||||
[RequestSizeLimit(long.MaxValue)]
|
[RequestSizeLimit(long.MaxValue)]
|
||||||
|
[Consumes("multipart/form-data")]
|
||||||
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
|
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
|
||||||
public async Task<IActionResult> UploadRom(List<IFormFile> files, long? OverridePlatformId = null)
|
public async Task<IActionResult> UploadRom(IFormFile file, long? OverridePlatformId = null)
|
||||||
{
|
{
|
||||||
Guid sessionid = Guid.NewGuid();
|
Guid sessionid = Guid.NewGuid();
|
||||||
|
|
||||||
string workPath = Path.Combine(Config.LibraryConfiguration.LibraryUploadDirectory, sessionid.ToString());
|
string workPath = Path.Combine(Config.LibraryConfiguration.LibraryUploadDirectory, sessionid.ToString());
|
||||||
|
|
||||||
long size = files.Sum(f => f.Length);
|
if (file.Length > 0)
|
||||||
|
|
||||||
List<Dictionary<string, object>> UploadedFiles = new List<Dictionary<string, object>>();
|
|
||||||
|
|
||||||
foreach (IFormFile formFile in files)
|
|
||||||
{
|
|
||||||
if (formFile.Length > 0)
|
|
||||||
{
|
{
|
||||||
Guid FileId = Guid.NewGuid();
|
Guid FileId = Guid.NewGuid();
|
||||||
|
|
||||||
string filePath = Path.Combine(workPath, Path.GetFileName(formFile.FileName));
|
string filePath = Path.Combine(workPath, Path.GetFileName(file.FileName));
|
||||||
|
|
||||||
if (!Directory.Exists(workPath))
|
if (!Directory.Exists(workPath))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(workPath);
|
Directory.CreateDirectory(workPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
||||||
|
|
||||||
using (var stream = System.IO.File.Create(filePath))
|
using (var stream = System.IO.File.Create(filePath))
|
||||||
{
|
{
|
||||||
await formFile.CopyToAsync(stream);
|
await file.CopyToAsync(stream);
|
||||||
|
|
||||||
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
|
||||||
UploadedFile.Add("id", FileId.ToString());
|
UploadedFile.Add("id", FileId.ToString());
|
||||||
UploadedFile.Add("originalname", Path.GetFileName(formFile.FileName));
|
UploadedFile.Add("originalname", Path.GetFileName(file.FileName));
|
||||||
UploadedFile.Add("fullpath", filePath);
|
UploadedFile.Add("fullpath", filePath);
|
||||||
UploadedFiles.Add(UploadedFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get override platform if specified
|
// get override platform if specified
|
||||||
IGDB.Models.Platform? OverridePlatform = null;
|
Platform? OverridePlatform = null;
|
||||||
if (OverridePlatformId != null)
|
if (OverridePlatformId != null)
|
||||||
{
|
{
|
||||||
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
|
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process uploaded files
|
// Process uploaded file
|
||||||
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
|
Classes.ImportGame uploadImport = new ImportGame();
|
||||||
|
Dictionary<string, object> RetVal = uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||||
|
switch (RetVal["type"])
|
||||||
{
|
{
|
||||||
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
case "rom":
|
||||||
|
if (RetVal["status"] == "imported")
|
||||||
|
{
|
||||||
|
gaseous_server.Models.Game? game = (gaseous_server.Models.Game)RetVal["game"];
|
||||||
|
if (game == null || game.Id == null)
|
||||||
|
{
|
||||||
|
RetVal["game"] = Games.GetGame(HasheousClient.Models.MetadataSources.IGDB, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Directory.Exists(workPath))
|
if (Directory.Exists(workPath))
|
||||||
@@ -85,7 +91,10 @@ namespace gaseous_server.Controllers
|
|||||||
Directory.Delete(workPath, true);
|
Directory.Delete(workPath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(new { count = files.Count, size });
|
return Ok(RetVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using IGDB;
|
using gaseous_server.Models;
|
||||||
using IGDB.Models;
|
using HasheousClient.Models.Metadata.IGDB;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NuGet.Common;
|
||||||
using static gaseous_server.Classes.Metadata.Games;
|
using static gaseous_server.Classes.Metadata.Games;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -33,40 +35,113 @@ namespace gaseous_server.Controllers
|
|||||||
|
|
||||||
private static async Task<List<Platform>> _SearchForPlatform(string SearchString)
|
private static async Task<List<Platform>> _SearchForPlatform(string SearchString)
|
||||||
{
|
{
|
||||||
string searchBody = "";
|
// search the database for the requested platforms
|
||||||
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
searchBody += "where name ~ *\"" + SearchString + "\"*;";
|
string query = "SELECT `Id` FROM Platform WHERE `Name` LIKE '%" + SearchString + "%';";
|
||||||
|
DataTable data = db.ExecuteCMD(query);
|
||||||
|
|
||||||
// get Platform metadata
|
List<Platform> platforms = new List<Platform>();
|
||||||
Communications comms = new Communications();
|
foreach (DataRow row in data.Rows)
|
||||||
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
|
{
|
||||||
|
Platform platform = Platforms.GetPlatform((long)row["Id"]);
|
||||||
|
|
||||||
return results.ToList();
|
platforms.Add(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
return platforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Game")]
|
[Route("Game")]
|
||||||
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<gaseous_server.Models.Game>), StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
|
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
|
||||||
{
|
{
|
||||||
List<Game> RetVal = await _SearchForGame(PlatformId, SearchString);
|
List<gaseous_server.Models.Game> RetVal = await _SearchForGame(PlatformId, SearchString);
|
||||||
return Ok(RetVal);
|
return Ok(RetVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
|
private static async Task<List<gaseous_server.Models.Game>> _SearchForGame(long PlatformId, string SearchString)
|
||||||
|
{
|
||||||
|
switch (Config.MetadataConfiguration.DefaultMetadataSource)
|
||||||
|
{
|
||||||
|
case HasheousClient.Models.MetadataSources.IGDB:
|
||||||
|
if (Config.IGDB.UseHasheousProxy == false)
|
||||||
{
|
{
|
||||||
string searchBody = "";
|
string searchBody = "";
|
||||||
string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
|
string searchFields = "fields *; ";
|
||||||
searchBody += "search \"" + SearchString + "\";";
|
searchBody += "search \"" + SearchString + "\";";
|
||||||
searchBody += "where platforms = (" + PlatformId + ");";
|
searchBody += "where platforms = (" + PlatformId + ");";
|
||||||
|
searchBody += "limit 100;";
|
||||||
|
|
||||||
// get Platform metadata
|
List<gaseous_server.Models.Game>? searchCache = Communications.GetSearchCache<List<gaseous_server.Models.Game>>(searchFields, searchBody);
|
||||||
|
|
||||||
|
if (searchCache == null)
|
||||||
|
{
|
||||||
|
// cache miss
|
||||||
|
// get Game metadata from data source
|
||||||
Communications comms = new Communications();
|
Communications comms = new Communications();
|
||||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
var results = await comms.APIComm<gaseous_server.Models.Game>("Game", searchFields, searchBody);
|
||||||
|
|
||||||
return results.ToList();
|
List<gaseous_server.Models.Game> games = new List<gaseous_server.Models.Game>();
|
||||||
|
foreach (gaseous_server.Models.Game game in results.ToList())
|
||||||
|
{
|
||||||
|
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus(HasheousClient.Models.MetadataSources.IGDB, "Game", (long)game.Id);
|
||||||
|
switch (cacheStatus)
|
||||||
|
{
|
||||||
|
case Storage.CacheStatus.NotPresent:
|
||||||
|
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, game, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Storage.CacheStatus.Expired:
|
||||||
|
Storage.NewCacheValue(HasheousClient.Models.MetadataSources.IGDB, game, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
games.Add(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
Communications.SetSearchCache<List<gaseous_server.Models.Game>>(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<gaseous_server.Models.Game> gamesToReturn = new List<gaseous_server.Models.Game>();
|
||||||
|
foreach (gaseous_server.Models.Game game in searchCache)
|
||||||
|
{
|
||||||
|
gaseous_server.Models.Game? tempGame = Games.GetGame(Communications.MetadataSource, (long)game.Id);
|
||||||
|
if (tempGame != null)
|
||||||
|
{
|
||||||
|
gamesToReturn.Add(tempGame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gamesToReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
|
||||||
|
Communications.ConfigureHasheousClient(ref hasheous);
|
||||||
|
List<gaseous_server.Models.Game> hSearch = hasheous.GetMetadataProxy_SearchGame<gaseous_server.Models.Game>(HasheousClient.Hasheous.MetadataProvider.IGDB, PlatformId.ToString(), SearchString).ToList<gaseous_server.Models.Game>();
|
||||||
|
|
||||||
|
List<gaseous_server.Models.Game> hGamesToReturn = new List<gaseous_server.Models.Game>();
|
||||||
|
foreach (gaseous_server.Models.Game game in hSearch)
|
||||||
|
{
|
||||||
|
hGamesToReturn.Add(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hGamesToReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new List<gaseous_server.Models.Game>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user