Compare commits
16 Commits
branch-v1.
...
main
Author | SHA1 | Date | |
---|---|---|---|
5f34a1b49f | |||
![]() |
2d63a1c416 | ||
![]() |
146597dd4b | ||
![]() |
f4ba84a54f | ||
![]() |
64fb76484b | ||
![]() |
bfade006bd | ||
![]() |
c8140d7178 | ||
![]() |
070589f718 | ||
![]() |
bb86cb52f6 | ||
![]() |
7dfb0b54eb | ||
![]() |
f0783fcae8 | ||
![]() |
68be24d514 | ||
![]() |
a5da1a9033 | ||
![]() |
fc09681cdd | ||
![]() |
6185912151 | ||
![]() |
deef919d5b |
@@ -1,4 +0,0 @@
|
||||
DATABASE_HOST=mariadb
|
||||
DATABASE_USER=root
|
||||
DATABASE_PASSWORD=gaseous
|
||||
DATABASE_DB=gaseous
|
@@ -2,5 +2,5 @@ FROM mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm
|
||||
|
||||
RUN apt-get update && apt-get install -y p7zip-full
|
||||
RUN mkdir -p /workspace/gaseous-server/wwwroot/emulators/EmulatorJS
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.11.7z
|
||||
RUN 7z x -y -o/workspace/gaseous-server/wwwroot/emulators/EmulatorJS 4.0.11.7z
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
|
||||
RUN 7z x -y -o/workspace/gaseous-server/wwwroot/emulators/EmulatorJS 4.0.12.7z
|
@@ -36,7 +36,9 @@
|
||||
"AndersEAndersen.html-class-suggestions",
|
||||
"george-alisson.html-preview-vscode",
|
||||
"ms-dotnettools.vscodeintellicode-csharp",
|
||||
"Zignd.html-css-class-completion"
|
||||
"Zignd.html-css-class-completion",
|
||||
"PWABuilder.pwa-studio",
|
||||
"ms-azuretools.vscode-docker"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
development:
|
||||
build:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
@@ -11,13 +11,15 @@ services:
|
||||
- dbhost=${DATABASE_HOST}
|
||||
- dbuser=${DATABASE_USER}
|
||||
- dbpass=${DATABASE_PASSWORD}
|
||||
- igdbclientid=<clientid>
|
||||
- igdbclientsecret=<clientsecret>
|
||||
- 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}
|
||||
- MARIADB_PASSWORD=${DATABASE_PASSWORD}
|
||||
|
51
.gitea/workflows/registry.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: release-tag
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
jobs:
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_ORG: sendnrw
|
||||
DOCKER_LATEST: latest
|
||||
RUNNER_TOOL_CACHE: /toolcache
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with: # replace it with your local IP
|
||||
config-inline: |
|
||||
[registry."git.send.nrw"]
|
||||
http = true
|
||||
insecure = true
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.send.nrw # replace it with your local IP
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./build/Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
push: true
|
||||
tags: | # replace it with your local IP and tags
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
@@ -9,9 +9,14 @@ on:
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Install dotnet tool
|
||||
@@ -21,18 +26,37 @@ jobs:
|
||||
- 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@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
- 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:${{ 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
|
40
.github/workflows/BuildDockerOnTag-Release.yml
vendored
@@ -8,9 +8,14 @@ on:
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Install dotnet tool
|
||||
@@ -20,18 +25,39 @@ jobs:
|
||||
- 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@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
- 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: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
@@ -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
|
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}}"
|
28
Dockerfile
@@ -1,28 +0,0 @@
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
|
||||
ARG TARGETARCH
|
||||
ARG BUILDPLATFORM
|
||||
WORKDIR /App
|
||||
EXPOSE 80
|
||||
|
||||
RUN echo "Target: $TARGETARCH"
|
||||
RUN echo "Build: $BUILDPLATFORM"
|
||||
|
||||
# Copy everything
|
||||
COPY . ./
|
||||
# Restore as distinct layers
|
||||
RUN dotnet restore "gaseous-server/gaseous-server.csproj" -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
|
||||
|
||||
# download and unzip EmulatorJS from CDN
|
||||
RUN apt-get update && apt-get install -y p7zip-full
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
|
||||
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
|
||||
|
||||
# Build runtime image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
ENV INDOCKER=1
|
||||
WORKDIR /App
|
||||
COPY --from=build-env /App/out .
|
||||
ENTRYPOINT ["dotnet", "gaseous-server.dll"]
|
@@ -21,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "screenshots", "screenshots"
|
||||
screenshots\Game.png = screenshots\Game.png
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-cli", "gaseous-cli\gaseous-cli.csproj", "{419CC4E4-8932-4E4A-B027-5521AA0CBA85}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -31,6 +33,10 @@ Global
|
||||
{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.Build.0 = Release|Any CPU
|
||||
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{419CC4E4-8932-4E4A-B027-5521AA0CBA85}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
39
README.MD
@@ -1,34 +1,30 @@
|
||||
[](https://github.com/gaseous-project/gaseous-server/actions/workflows/dotnet.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/codeql.yml) [](https://github.com/gaseous-project/gaseous-server/actions/workflows/BuildDockerOnTag-Release.yml)
|
||||
# Gaseous Server
|
||||
[](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 ROMs.
|
||||
|
||||
## Warning
|
||||
|
||||
Versions 1.6.1 and earlier are not suitable for being exposed to the internet, as:
|
||||
1. there is no authentication support, meaning anyone could trash your library
|
||||
2. the server has not been hardened for exposure to the internet - so there maybe unknown vulnerabilities
|
||||
|
||||
If you expose one of these earlier versions of the server to the internet, **you do so at your own risk**.
|
||||
|
||||
Version 1.7.0 and later contain user authentication, and can be exposed to the internet. However, it is recommended to no expose the server to the internet if you're not actively using it remotely, or if you have alternative means to access it remotely like a VPN.
|
||||
Version 1.7.0 and later contain user authentication, and can be exposed to the internet. However, it is recommended to not expose the server to the internet if you're not actively using it remotely, or if you have alternative means to access it remotely like a VPN.
|
||||
|
||||
While we do our best to stay on top of server security, if you expose the server to the internet **you do so at your own risk**.
|
||||
|
||||
## Screenshots
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Requirements
|
||||
* MariaDB 11.1.2 (preferred) or MySQL Server 8+
|
||||
* MariaDB 11.1.2+ (preferred) or MySQL Server 8+
|
||||
* These are the database versions Gaseous has been tested and developed against. Your mileage may vary with earlier versions.
|
||||
* MariaDB is the preferred database server, while MySQL will continue to be supported for existing users (they should be interchangable).
|
||||
* Note that due to the earlier database schema using MySQL specific features, moving to MariaDB from MySQL will require rebuilding your database from scratch. The "Library Scan" background task can be used to re-import all titles.
|
||||
* Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation
|
||||
|
||||
If using the provided docker-compose.yml, MariaDB will be installed for you.
|
||||
# Installation
|
||||
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
|
||||
|
||||
# Adding Content
|
||||
1. Import signatures: see https://github.com/gaseous-project/gaseous-server/wiki/Signatures
|
||||
2. Add ROMs: see https://github.com/gaseous-project/gaseous-server/wiki/Adding-ROMs
|
||||
|
||||
## Friends of Gaseous
|
||||
* [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS): A fantastic (and fast) Javascript based implementation of RetroArch, supporting a wide variety of platforms. Discord: https://discord.gg/6akryGkETU
|
||||
@@ -43,11 +39,4 @@ The following projects are used by Gaseous
|
||||
* [EmulatorJS](https://github.com/EmulatorJS/EmulatorJS)
|
||||
|
||||
## Discord Server
|
||||
[](https://discord.gg/Nhu7wpT3k4)
|
||||
|
||||
# Installation
|
||||
See https://github.com/gaseous-project/gaseous-server/wiki/Installation for installation instructions.
|
||||
|
||||
# Adding Content
|
||||
1. Import signatures: see https://github.com/gaseous-project/gaseous-server/wiki/Signatures
|
||||
2. Add ROMs: see https://github.com/gaseous-project/gaseous-server/wiki/Adding-ROMs
|
||||
Join our Discord server: https://discord.gg/Nhu7wpT3k4
|
||||
|
73
build/Dockerfile
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
|
||||
|
||||
# Build Gaseous CLI
|
||||
# Restore as distinct layers
|
||||
RUN dotnet restore "gaseous-cli/gaseous-cli.csproj" -a $TARGETARCH
|
||||
# Build and publish a release
|
||||
RUN dotnet publish "gaseous-cli/gaseous-cli.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
|
||||
|
||||
# update apt-get
|
||||
RUN apt-get update
|
||||
|
||||
# # download and unzip EmulatorJS from CDN
|
||||
RUN apt-get install -y p7zip-full
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
|
||||
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
|
||||
|
||||
# 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" ]
|
79
build/Dockerfile-EmbeddedDB
Normal file
@@ -0,0 +1,79 @@
|
||||
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
|
||||
|
||||
# Build Gaseous CLI
|
||||
# Restore as distinct layers
|
||||
RUN dotnet restore "gaseous-cli/gaseous-cli.csproj" -a $TARGETARCH
|
||||
# Build and publish a release
|
||||
RUN dotnet publish "gaseous-cli/gaseous-cli.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
|
||||
|
||||
# update apt-get
|
||||
RUN apt-get update
|
||||
|
||||
# # download and unzip EmulatorJS from CDN
|
||||
RUN apt-get install -y p7zip-full
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
|
||||
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
|
||||
|
||||
# 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./build/Dockerfile
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
@@ -12,7 +13,7 @@ services:
|
||||
ports:
|
||||
- 5198:80
|
||||
volumes:
|
||||
- gs:/root/.gaseous-server
|
||||
- gs:/home/gaseous/.gaseous-server
|
||||
environment:
|
||||
- TZ=Australia/Sydney
|
||||
- dbhost=gsdb
|
||||
|
@@ -1,39 +0,0 @@
|
||||
version: '2'
|
||||
services:
|
||||
gaseous-server:
|
||||
container_name: gaseous-server
|
||||
image: gaseousgames/gaseousserver:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
depends_on:
|
||||
- gsdb
|
||||
ports:
|
||||
- 5198:80
|
||||
volumes:
|
||||
- gs:/root/.gaseous-server
|
||||
environment:
|
||||
- TZ=Australia/Sydney
|
||||
- dbhost=gsdb
|
||||
- dbuser=root
|
||||
- dbpass=gaseous
|
||||
- igdbclientid=<clientid>
|
||||
- igdbclientsecret=<clientsecret>
|
||||
gsdb:
|
||||
container_name: gsdb
|
||||
image: mariadb
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gaseous
|
||||
volumes:
|
||||
- gsdb:/var/lib/mysql
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=gaseous
|
||||
- MARIADB_USER=gaseous
|
||||
- MARIADB_PASSWORD=gaseous
|
||||
networks:
|
||||
gaseous:
|
||||
driver: bridge
|
||||
volumes:
|
||||
gs:
|
||||
gsdb:
|
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
@@ -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
@@ -12,6 +12,6 @@ namespace Authentication
|
||||
{
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
public Guid ProfileId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ namespace Authentication
|
||||
/// <summary>
|
||||
/// Class that represents the Role table in the MySQL Database
|
||||
/// </summary>
|
||||
public class RoleTable
|
||||
public class RoleTable
|
||||
{
|
||||
private Database _database;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Authentication
|
||||
parameters.Add("@id", roleId);
|
||||
|
||||
DataTable table = _database.ExecuteCMD(commandText, parameters);
|
||||
|
||||
|
||||
if (table.Rows.Count == 0)
|
||||
{
|
||||
return null;
|
||||
@@ -104,7 +104,7 @@ namespace Authentication
|
||||
var roleName = GetRoleName(roleId);
|
||||
ApplicationRole? role = null;
|
||||
|
||||
if(roleName != null)
|
||||
if (roleName != null)
|
||||
{
|
||||
role = new ApplicationRole();
|
||||
role.Id = roleId;
|
||||
@@ -153,7 +153,7 @@ namespace Authentication
|
||||
string commandText = "Select Name from Roles";
|
||||
|
||||
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));
|
||||
role.Id = (string)row["Id"];
|
||||
|
@@ -82,7 +82,7 @@ namespace Authentication
|
||||
parameters.Add("providerKey", userLogin.ProviderKey);
|
||||
|
||||
DataTable table = _database.ExecuteCMD(commandText, parameters);
|
||||
|
||||
|
||||
if (table.Rows.Count == 0)
|
||||
{
|
||||
return null;
|
||||
|
@@ -35,7 +35,7 @@ namespace Authentication
|
||||
parameters.Add("@userId", userId);
|
||||
|
||||
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
|
||||
foreach(DataRow row in rows)
|
||||
foreach (DataRow row in rows)
|
||||
{
|
||||
roles.Add((string)row["Name"]);
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ namespace Authentication
|
||||
/// Class that represents the Users table in the MySQL Database
|
||||
/// </summary>
|
||||
public class UserTable<TUser>
|
||||
where TUser :ApplicationUser
|
||||
where TUser : ApplicationUser
|
||||
{
|
||||
private Database _database;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Authentication
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
|
||||
|
||||
DataTable table = _database.ExecuteCMD(commandText, parameters);
|
||||
|
||||
|
||||
if (table.Rows.Count == 0)
|
||||
{
|
||||
return null;
|
||||
@@ -75,7 +75,7 @@ namespace Authentication
|
||||
public TUser GetUserById(string userId)
|
||||
{
|
||||
TUser user = null;
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId 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 } };
|
||||
|
||||
var rows = _database.ExecuteCMDDict(commandText, parameters);
|
||||
@@ -89,7 +89,7 @@ namespace Authentication
|
||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||
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.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||
@@ -97,10 +97,10 @@ namespace Authentication
|
||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||
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.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||
}
|
||||
|
||||
return user;
|
||||
@@ -114,11 +114,11 @@ namespace Authentication
|
||||
public List<TUser> GetUserByName(string normalizedUserName)
|
||||
{
|
||||
List<TUser> users = new List<TUser>();
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId 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 } };
|
||||
|
||||
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));
|
||||
user.Id = (string)row["Id"];
|
||||
@@ -127,7 +127,7 @@ namespace Authentication
|
||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||
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.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||
@@ -135,10 +135,10 @@ namespace Authentication
|
||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||
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.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
@@ -148,10 +148,10 @@ namespace Authentication
|
||||
public List<TUser> GetUsers()
|
||||
{
|
||||
List<TUser> users = new List<TUser>();
|
||||
string commandText = "Select * from Users LEFT JOIN (SELECT UserId, Id AS AvatarId FROM UserAvatars) UserAvatars ON Users.Id = UserAvatars.UserId 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);
|
||||
foreach(Dictionary<string, object> row in rows)
|
||||
foreach (Dictionary<string, object> row in rows)
|
||||
{
|
||||
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
|
||||
user.Id = (string)row["Id"];
|
||||
@@ -160,7 +160,7 @@ namespace Authentication
|
||||
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
|
||||
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
|
||||
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.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
|
||||
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
|
||||
@@ -168,10 +168,10 @@ namespace Authentication
|
||||
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
|
||||
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.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
|
||||
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true : false;
|
||||
user.SecurityProfile = GetSecurityProfile(user);
|
||||
user.UserPreferences = GetPreferences(user);
|
||||
user.Avatar = string.IsNullOrEmpty((string?)row["AvatarId"]) ? Guid.Empty : Guid.Parse((string?)row["AvatarId"]);
|
||||
user.ProfileId = string.IsNullOrEmpty((string?)row["ProfileId"]) ? Guid.Empty : Guid.Parse((string?)row["ProfileId"]);
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
@@ -258,10 +258,11 @@ namespace Authentication
|
||||
/// <returns></returns>
|
||||
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>();
|
||||
parameters.Add("@name", user.UserName);
|
||||
parameters.Add("@id", user.Id);
|
||||
parameters.Add("@profileId", Guid.NewGuid());
|
||||
parameters.Add("@pwdHash", user.PasswordHash);
|
||||
parameters.Add("@SecStamp", user.SecurityStamp);
|
||||
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
|
||||
@@ -292,7 +293,7 @@ namespace Authentication
|
||||
/// <returns></returns>
|
||||
private int Delete(string userId)
|
||||
{
|
||||
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId; Delete from GameState where UserId = @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>();
|
||||
parameters.Add("@userId", userId);
|
||||
|
||||
@@ -376,7 +377,7 @@ namespace Authentication
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters.Add("Id", user.Id);
|
||||
parameters.Add("SecurityProfile", Newtonsoft.Json.JsonConvert.SerializeObject(securityProfile));
|
||||
|
||||
|
||||
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
|
||||
}
|
||||
|
||||
@@ -408,7 +409,7 @@ namespace Authentication
|
||||
List<UserPreferenceViewModel> userPreferences = GetPreferences(user);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
|
||||
foreach (UserPreferenceViewModel modelItem in model)
|
||||
{
|
||||
bool prefItemFound = false;
|
||||
@@ -449,7 +450,7 @@ namespace Authentication
|
||||
{
|
||||
{ "userid", user.Id }
|
||||
};
|
||||
|
||||
|
||||
if (bytes.Length == 0)
|
||||
{
|
||||
sql = "DELETE FROM UserAvatars WHERE UserId = @userid";
|
||||
|
@@ -8,8 +8,9 @@ namespace Authentication
|
||||
public List<String> Roles { get; set; }
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public List<UserPreferenceViewModel> UserPreferences { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
public string HighestRole {
|
||||
public Guid ProfileId { get; set; }
|
||||
public string HighestRole
|
||||
{
|
||||
get
|
||||
{
|
||||
string _highestRole = "";
|
||||
|
@@ -8,38 +8,46 @@ namespace Authentication
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
public List<string> Roles { get; set; }
|
||||
public SecurityProfileViewModel SecurityProfile { get; set; }
|
||||
public Guid Avatar { get; set; }
|
||||
public string HighestRole {
|
||||
public Guid ProfileId { get; set; }
|
||||
public string HighestRole
|
||||
{
|
||||
get
|
||||
{
|
||||
string _highestRole = "";
|
||||
foreach (string role in Roles)
|
||||
if (Roles != null)
|
||||
{
|
||||
switch (role)
|
||||
foreach (string role in Roles)
|
||||
{
|
||||
case "Admin":
|
||||
// there is no higher
|
||||
_highestRole = role;
|
||||
break;
|
||||
case "Gamer":
|
||||
// only one high is Admin, so check for that
|
||||
if (_highestRole != "Admin")
|
||||
{
|
||||
switch (role)
|
||||
{
|
||||
case "Admin":
|
||||
// there is no higher
|
||||
_highestRole = role;
|
||||
}
|
||||
break;
|
||||
case "Player":
|
||||
// make sure _highestRole isn't already set to Gamer or Admin
|
||||
if (_highestRole != "Admin" && _highestRole != "Gamer")
|
||||
{
|
||||
_highestRole = role;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_highestRole = "Player";
|
||||
break;
|
||||
break;
|
||||
case "Gamer":
|
||||
// only one high is Admin, so check for that
|
||||
if (_highestRole != "Admin")
|
||||
{
|
||||
_highestRole = role;
|
||||
}
|
||||
break;
|
||||
case "Player":
|
||||
// make sure _highestRole isn't already set to Gamer or Admin
|
||||
if (_highestRole != "Admin" && _highestRole != "Gamer")
|
||||
{
|
||||
_highestRole = role;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_highestRole = "Player";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_highestRole = "Player";
|
||||
}
|
||||
|
||||
return _highestRole;
|
||||
}
|
||||
|
@@ -4,12 +4,39 @@ using System.Security.Cryptography;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Bios
|
||||
{
|
||||
public Bios()
|
||||
{
|
||||
|
||||
}
|
||||
public class Bios
|
||||
{
|
||||
public Bios()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -96,10 +123,11 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformslug, base.filename);
|
||||
return Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios");
|
||||
}
|
||||
}
|
||||
public bool Available {
|
||||
public bool Available
|
||||
{
|
||||
get
|
||||
{
|
||||
bool fileExists = File.Exists(biosPath);
|
||||
|
@@ -45,11 +45,13 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
var xmlStream = File.OpenRead(FileName);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Hash File", "Generating MD5 hash for file: " + FileName);
|
||||
var md5 = MD5.Create();
|
||||
byte[] md5HashByte = md5.ComputeHash(xmlStream);
|
||||
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
|
||||
_md5hash = md5Hash;
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Hash File", "Generating SHA1 hash for file: " + FileName);
|
||||
var sha1 = SHA1.Create();
|
||||
xmlStream.Position = 0;
|
||||
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
|
||||
|
@@ -154,8 +154,8 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Using configuration:");
|
||||
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(_config, Formatting.Indented));
|
||||
// Console.WriteLine("Using configuration:");
|
||||
// Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(_config, Formatting.Indented));
|
||||
}
|
||||
|
||||
public static void UpdateConfig()
|
||||
@@ -197,17 +197,12 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
string SettingName = (string)dataRow["Setting"];
|
||||
|
||||
if (SettingName.StartsWith("LastRun_"))
|
||||
{
|
||||
Console.WriteLine("Break");
|
||||
}
|
||||
|
||||
if (AppSettings.ContainsKey(SettingName))
|
||||
{
|
||||
AppSettings.Remove(SettingName);
|
||||
}
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
|
||||
// Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -536,11 +531,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadSetting<string>("LibraryRootDirectory", Path.Combine(Config.ConfigurationPath, "Data"));
|
||||
}
|
||||
set
|
||||
{
|
||||
SetSetting<string>("LibraryRootDirectory", value);
|
||||
return Path.Combine(Config.ConfigurationPath, "Data");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,6 +575,14 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public string LibraryFirmwareDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(LibraryRootDirectory, "Firmware");
|
||||
}
|
||||
}
|
||||
|
||||
public string LibraryUploadDirectory
|
||||
{
|
||||
get
|
||||
@@ -665,7 +664,8 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
|
||||
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
|
||||
if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
|
||||
// if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
|
||||
if (!Directory.Exists(LibraryFirmwareDirectory)) { Directory.CreateDirectory(LibraryFirmwareDirectory); }
|
||||
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
|
||||
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
|
||||
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
|
||||
|
@@ -23,7 +23,7 @@ namespace gaseous_server.Classes
|
||||
_schema_version = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Database()
|
||||
{
|
||||
|
||||
@@ -70,39 +70,41 @@ namespace gaseous_server.Classes
|
||||
|
||||
public void InitDB()
|
||||
{
|
||||
// load resources
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
// load resources
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
switch (_ConnectorType)
|
||||
DatabaseMemoryCacheOptions? CacheOptions = new DatabaseMemoryCacheOptions(false);
|
||||
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
// check if the database exists first - first run must have permissions to create a database
|
||||
string sql = "CREATE DATABASE IF NOT EXISTS `" + Config.DatabaseConfiguration.DatabaseName + "`;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
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
|
||||
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)
|
||||
{
|
||||
// no schema table present - create 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);";
|
||||
ExecuteCMD(sql, dbDict);
|
||||
// no schema table present - create 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);";
|
||||
ExecuteCMD(sql, dbDict, CacheOptions);
|
||||
}
|
||||
|
||||
sql = "SELECT schema_version FROM schema_version;";
|
||||
dbDict = new Dictionary<string, object>();
|
||||
DataTable SchemaVersion = ExecuteCMD(sql, dbDict);
|
||||
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++)
|
||||
for (int i = OuterSchemaVer; i < 10000; i++)
|
||||
{
|
||||
string resourceName = "gaseous_server.Support.Database.MySQL.gaseous-" + i + ".sql";
|
||||
string dbScript = "";
|
||||
@@ -113,39 +115,39 @@ namespace gaseous_server.Classes
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
dbScript = reader.ReadToEnd();
|
||||
dbScript = reader.ReadToEnd();
|
||||
|
||||
// apply script
|
||||
sql = "SELECT schema_version FROM schema_version;";
|
||||
dbDict = new Dictionary<string, object>();
|
||||
SchemaVersion = ExecuteCMD(sql, dbDict);
|
||||
SchemaVersion = ExecuteCMD(sql, dbDict, CacheOptions);
|
||||
if (SchemaVersion.Rows.Count == 0)
|
||||
{
|
||||
// something is broken here... where's the table?
|
||||
Logging.Log(Logging.LogType.Critical, "Database", "Schema table missing! This shouldn't happen!");
|
||||
throw new Exception("schema_version table is missing!");
|
||||
// something is broken here... where's the table?
|
||||
Logging.Log(Logging.LogType.Critical, "Database", "Schema table missing! This shouldn't happen!");
|
||||
throw new Exception("schema_version table is missing!");
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
|
||||
|
||||
|
||||
// apply schema!
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
|
||||
ExecuteCMD(dbScript, dbDict, 180);
|
||||
ExecuteCMD(dbScript, dbDict, CacheOptions, 180);
|
||||
|
||||
sql = "UPDATE schema_version SET schema_version=@schemaver";
|
||||
dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("schemaver", i);
|
||||
ExecuteCMD(sql, dbDict);
|
||||
ExecuteCMD(sql, dbDict, CacheOptions);
|
||||
|
||||
// run post-upgrade code
|
||||
DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
|
||||
@@ -162,47 +164,91 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Database setup complete");
|
||||
break;
|
||||
}
|
||||
Logging.Log(Logging.LogType.Information, "Database", "Database setup complete");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public DataTable ExecuteCMD(string Command)
|
||||
public DataTable ExecuteCMD(string Command)
|
||||
{
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
return _ExecuteCMD(Command, dbDict, 30, "");
|
||||
return _ExecuteCMD(Command, dbDict, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters)
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, 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, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
||||
}
|
||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters)
|
||||
{
|
||||
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 = "")
|
||||
{
|
||||
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)
|
||||
{
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
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, Dictionary<string, object> Parameters)
|
||||
{
|
||||
return _ExecuteCMDDict(Command, Parameters, 30, "");
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteCMDDict(Command, Parameters, 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, DatabaseMemoryCacheOptions? CacheOptions)
|
||||
{
|
||||
DataTable dataTable = _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
||||
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)
|
||||
{
|
||||
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 = "")
|
||||
{
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
return _ExecuteCMDDict(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string 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
|
||||
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
|
||||
@@ -228,18 +274,49 @@ namespace gaseous_server.Classes
|
||||
return rows;
|
||||
}
|
||||
|
||||
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
return (DataTable)conn.ExecCMD(Command, Parameters, Timeout);
|
||||
default:
|
||||
return new DataTable();
|
||||
}
|
||||
}
|
||||
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; }
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
DataTable RetTable = conn.ExecCMD(Command, Parameters, Timeout);
|
||||
if (CacheOptions is object && CacheOptions.CacheEnabled)
|
||||
{
|
||||
DatabaseMemoryCache.SetCacheObject(CacheKey, RetTable, CacheOptions.ExpirationSeconds);
|
||||
}
|
||||
return RetTable;
|
||||
default:
|
||||
return new DataTable();
|
||||
}
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string Command)
|
||||
{
|
||||
@@ -247,52 +324,194 @@ namespace gaseous_server.Classes
|
||||
return _ExecuteNonQuery(Command, dbDict, 30, "");
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters)
|
||||
{
|
||||
return _ExecuteNonQuery(Command, Parameters, 30, "");
|
||||
}
|
||||
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters)
|
||||
{
|
||||
return _ExecuteNonQuery(Command, Parameters, 30, "");
|
||||
}
|
||||
|
||||
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteNonQuery(Command, Parameters, Timeout, ConnectionString);
|
||||
}
|
||||
}
|
||||
|
||||
private int _ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
|
||||
return retVal;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
private int _ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
int retVal = conn.ExecNonQuery(Command, Parameters, Timeout);
|
||||
return retVal;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60)
|
||||
{
|
||||
object conn;
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
{
|
||||
var commands = new List<Dictionary<string, object>>();
|
||||
foreach (SQLTransactionItem CommandItem in CommandList)
|
||||
{
|
||||
var nCmd = new Dictionary<string, object>();
|
||||
nCmd.Add("sql", CommandItem.SQLCommand);
|
||||
nCmd.Add("values", CommandItem.Parameters);
|
||||
commands.Add(nCmd);
|
||||
}
|
||||
public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60)
|
||||
{
|
||||
object conn;
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
{
|
||||
var commands = new List<Dictionary<string, object>>();
|
||||
foreach (SQLTransactionItem CommandItem in CommandList)
|
||||
{
|
||||
var nCmd = new Dictionary<string, object>();
|
||||
nCmd.Add("sql", CommandItem.SQLCommand);
|
||||
nCmd.Add("values", CommandItem.Parameters);
|
||||
commands.Add(nCmd);
|
||||
}
|
||||
|
||||
conn = new MySQLServerConnector(_ConnectionString);
|
||||
((MySQLServerConnector)conn).TransactionExecCMD(commands, Timeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
conn = new MySQLServerConnector(_ConnectionString);
|
||||
((MySQLServerConnector)conn).TransactionExecCMD(commands, Timeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
@@ -309,7 +528,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
return (int)SchemaVersion.Rows[0][0];
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
return 0;
|
||||
|
||||
@@ -319,17 +538,17 @@ namespace gaseous_server.Classes
|
||||
public bool TestConnection()
|
||||
{
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(_ConnectionString);
|
||||
return conn.TestConnection();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(_ConnectionString);
|
||||
return conn.TestConnection();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class SQLTransactionItem
|
||||
{
|
||||
public class SQLTransactionItem
|
||||
{
|
||||
public SQLTransactionItem()
|
||||
{
|
||||
|
||||
@@ -341,11 +560,11 @@ namespace gaseous_server.Classes
|
||||
this.Parameters = Parameters;
|
||||
}
|
||||
|
||||
public string? SQLCommand;
|
||||
public Dictionary<string, object>? Parameters = new Dictionary<string, object>();
|
||||
}
|
||||
public string? SQLCommand;
|
||||
public Dictionary<string, object>? Parameters = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private partial class MySQLServerConnector
|
||||
private partial class MySQLServerConnector
|
||||
{
|
||||
private string DBConn = "";
|
||||
|
||||
@@ -358,8 +577,8 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
DataTable RetTable = new DataTable();
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
@@ -384,7 +603,9 @@ namespace gaseous_server.Classes
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
||||
}
|
||||
RetTable.Load(cmd.ExecuteReader());
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
||||
Trace.WriteLine("Error executing " + SQL);
|
||||
Trace.WriteLine("Full exception: " + ex.ToString());
|
||||
@@ -397,12 +618,12 @@ namespace gaseous_server.Classes
|
||||
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;
|
||||
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
@@ -427,7 +648,9 @@ namespace gaseous_server.Classes
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
|
||||
}
|
||||
result = cmd.ExecuteNonQuery();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
|
||||
Trace.WriteLine("Error executing " + SQL);
|
||||
Trace.WriteLine("Full exception: " + ex.ToString());
|
||||
@@ -440,10 +663,10 @@ namespace gaseous_server.Classes
|
||||
return result;
|
||||
}
|
||||
|
||||
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
|
||||
{
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
|
||||
{
|
||||
using (MySqlConnection conn = new MySqlConnection(DBConn))
|
||||
{
|
||||
conn.Open();
|
||||
var command = conn.CreateCommand();
|
||||
MySqlTransaction transaction;
|
||||
@@ -460,28 +683,28 @@ namespace gaseous_server.Classes
|
||||
transaction.Commit();
|
||||
conn.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
|
||||
{
|
||||
var cmd = new MySqlCommand();
|
||||
cmd.Connection = Conn;
|
||||
cmd.CommandText = SQL;
|
||||
cmd.CommandTimeout = Timeout;
|
||||
{
|
||||
var withBlock = cmd.Parameters;
|
||||
if (Parameters is object)
|
||||
{
|
||||
if (Parameters.Count > 0)
|
||||
{
|
||||
foreach (string param in Parameters.Keys)
|
||||
withBlock.AddWithValue(param, Parameters[param]);
|
||||
}
|
||||
}
|
||||
}
|
||||
private MySqlCommand buildcommand(MySqlConnection Conn, string SQL, Dictionary<string, object> Parameters, int Timeout)
|
||||
{
|
||||
var cmd = new MySqlCommand();
|
||||
cmd.Connection = Conn;
|
||||
cmd.CommandText = SQL;
|
||||
cmd.CommandTimeout = Timeout;
|
||||
{
|
||||
var withBlock = cmd.Parameters;
|
||||
if (Parameters is object)
|
||||
{
|
||||
if (Parameters.Count > 0)
|
||||
{
|
||||
foreach (string param in Parameters.Keys)
|
||||
withBlock.AddWithValue(param, Parameters[param]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public bool TestConnection()
|
||||
{
|
||||
@@ -499,7 +722,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -109,7 +109,7 @@ namespace gaseous_server.Classes
|
||||
db.ExecuteNonQuery(sql);
|
||||
break;
|
||||
|
||||
case 1022:
|
||||
case 1023:
|
||||
// load country list
|
||||
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding country look up table contents");
|
||||
|
||||
@@ -149,9 +149,102 @@ namespace gaseous_server.Classes
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
} while (reader.EndOfStream == false);
|
||||
}
|
||||
break;
|
||||
|
||||
// this is a safe background task
|
||||
BackgroundUpgradeTargetSchemaVersions.Add(1022);
|
||||
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 = (bool)row["DefaultLibrary"];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// migrating metadata is a safe background task
|
||||
BackgroundUpgradeTargetSchemaVersions.Add(1024);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -168,8 +261,8 @@ namespace gaseous_server.Classes
|
||||
MySql_1002_MigrateMetadataVersion();
|
||||
break;
|
||||
|
||||
case 1022:
|
||||
MySql_1022_MigrateMetadataVersion();
|
||||
case 1024:
|
||||
MySql_1024_MigrateMetadataVersion();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -272,16 +365,17 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static void MySql_1022_MigrateMetadataVersion()
|
||||
public static void MySql_1024_MigrateMetadataVersion()
|
||||
{
|
||||
FileSignature fileSignature = new FileSignature();
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM Games_Roms WHERE RomDataVersion = 1;";
|
||||
string sql = "SELECT * FROM view_Games_Roms WHERE RomDataVersion = 1;";
|
||||
DataTable data = db.ExecuteCMD(sql);
|
||||
long count = 1;
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Database Migration", "Updating ROM table for ROM: " + (string)row["Name"]);
|
||||
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()
|
||||
@@ -300,6 +394,8 @@ namespace gaseous_server.Classes
|
||||
Game game = Games.GetGame((long)row["GameId"], false, false, false);
|
||||
|
||||
ImportGame.StoreROM(library, hash, game, platform, signature, (string)row["Path"], (long)row["Id"]);
|
||||
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using HasheousClient.Models;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
@@ -192,37 +193,45 @@ namespace gaseous_server.Classes
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Checking signature for file: " + GameFileImportPath + "\nMD5 hash: " + hash.md5hash + "\nSHA1 hash: " + hash.sha1hash);
|
||||
|
||||
|
||||
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
|
||||
gaseous_server.Models.Signatures_Games? discoveredSignature = null;
|
||||
|
||||
// do database search first
|
||||
gaseous_server.Models.Signatures_Games? dbSignature = _GetFileSignatureFromDatabase(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
if (dbSignature != null)
|
||||
// begin signature search
|
||||
switch (Config.MetadataConfiguration.SignatureSource)
|
||||
{
|
||||
// local signature found
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature found in local database for game: " + dbSignature.Game.Name);
|
||||
discoveredSignature = dbSignature;
|
||||
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;
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
if (discoveredSignature == null)
|
||||
{
|
||||
// no local signature attempt to pull from Hasheous
|
||||
dbSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
// 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");
|
||||
|
||||
if (dbSignature != null)
|
||||
{
|
||||
// signature retrieved from Hasheous
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
|
||||
discoveredSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
else
|
||||
{
|
||||
// construct a signature from file data
|
||||
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
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);
|
||||
@@ -290,65 +299,87 @@ namespace gaseous_server.Classes
|
||||
MD5 = hash.md5hash,
|
||||
SHA1 = hash.sha1hash
|
||||
});
|
||||
|
||||
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)
|
||||
{
|
||||
if (metadataResult.Id.Length > 0)
|
||||
{
|
||||
switch (metadataResult.Source)
|
||||
{
|
||||
case HasheousClient.Models.MetadataSources.IGDB:
|
||||
signature.Flags.IGDBPlatformId = (long)Platforms.GetPlatform(metadataResult.Id, false).Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get game metadata
|
||||
if (HasheousResult.Metadata != null)
|
||||
{
|
||||
if (HasheousResult.Metadata.Count > 0)
|
||||
{
|
||||
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
|
||||
{
|
||||
if (metadataResult.Id.Length > 0)
|
||||
{
|
||||
switch (metadataResult.Source)
|
||||
{
|
||||
case HasheousClient.Models.MetadataSources.IGDB:
|
||||
signature.Flags.IGDBGameId = (long)Games.GetGame(metadataResult.Id, false, false, false).Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Import Game", "An error occurred while importing " + ImageName, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (metadataResult.Id.Length > 0)
|
||||
{
|
||||
switch (metadataResult.Source)
|
||||
{
|
||||
case HasheousClient.Models.MetadataSources.IGDB:
|
||||
signature.Flags.IGDBPlatformId = (long)Platforms.GetPlatform(metadataResult.Id, false).Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get game metadata
|
||||
if (HasheousResult.Metadata != null)
|
||||
{
|
||||
if (HasheousResult.Metadata.Count > 0)
|
||||
{
|
||||
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
|
||||
{
|
||||
if (metadataResult.Id.Length > 0)
|
||||
{
|
||||
switch (metadataResult.Source)
|
||||
{
|
||||
case HasheousClient.Models.MetadataSources.IGDB:
|
||||
signature.Flags.IGDBGameId = (long)Games.GetGame(metadataResult.Id, false, false, false).Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
// platforms
|
||||
List<FilterItem> platforms = new List<FilterItem>();
|
||||
|
||||
|
||||
string ageRestriction_Platform = "AgeGroup.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
||||
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
|
||||
if (IncludeUnrated == true)
|
||||
@@ -24,9 +24,9 @@ namespace gaseous_server.Classes
|
||||
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
|
||||
}
|
||||
|
||||
string sql = "SELECT Platform.Id, Platform.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, Games_Roms.PlatformId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id , Games_Roms.PlatformId HAVING RomCount > 0) Game JOIN Platform ON Game.PlatformId = Platform.Id GROUP BY Platform.`Name`;";
|
||||
|
||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
||||
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 GROUP BY Platform.`Name`;";
|
||||
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
foreach (DataRow dr in dbResponse.Rows)
|
||||
{
|
||||
@@ -82,8 +82,8 @@ namespace gaseous_server.Classes
|
||||
|
||||
// age groups
|
||||
List<FilterItem> agegroupings = new List<FilterItem>();
|
||||
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
|
||||
dbResponse = db.ExecuteCMD(sql);
|
||||
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(view_Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
|
||||
dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
foreach (DataRow dr in dbResponse.Rows)
|
||||
{
|
||||
@@ -112,9 +112,9 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
//string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id HAVING GameCount > 0 ORDER BY <ITEMNAME>.`Name`;";
|
||||
|
||||
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + AgeRestriction + ") GROUP BY Game.Id HAVING RomCount > 0) Game JOIN Relation_Game_<ITEMNAME>s ON Game.Id = Relation_Game_<ITEMNAME>s.GameId JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
|
||||
string sql = "SELECT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT 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 JOIN <ITEMNAME> ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id GROUP BY <ITEMNAME>.`Name` ORDER BY <ITEMNAME>.`Name`;";
|
||||
sql = sql.Replace("<ITEMNAME>", Name);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
return dbResponse;
|
||||
}
|
||||
|
@@ -7,37 +7,37 @@ using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
|
||||
|
||||
namespace gaseous_server
|
||||
{
|
||||
public static class GameLibrary
|
||||
{
|
||||
public static class GameLibrary
|
||||
{
|
||||
// exceptions
|
||||
public class PathExists : Exception
|
||||
{
|
||||
{
|
||||
public PathExists(string path) : base("The library path " + path + " already exists.")
|
||||
{}
|
||||
{ }
|
||||
}
|
||||
|
||||
public class PathNotFound : Exception
|
||||
{
|
||||
public PathNotFound(string path) : base("The path " + path + " does not exist.")
|
||||
{}
|
||||
{ }
|
||||
}
|
||||
|
||||
public class LibraryNotFound : Exception
|
||||
{
|
||||
public LibraryNotFound(int LibraryId) : base("Library id " + LibraryId + " does not exist.")
|
||||
{}
|
||||
{ }
|
||||
}
|
||||
|
||||
public class CannotDeleteDefaultLibrary : Exception
|
||||
{
|
||||
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
|
||||
@@ -60,13 +60,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
|
||||
{
|
||||
get
|
||||
{
|
||||
List<LibraryItem> libraryItems = new List<LibraryItem>();
|
||||
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);
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
@@ -113,7 +125,7 @@ namespace gaseous_server
|
||||
dbDict.Add("path", PathName);
|
||||
dbDict.Add("defaultplatform", DefaultPlatformId);
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
|
||||
int newLibraryId = (int)(long)data.Rows[0][0];
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Management", "Created library " + Name + " at directory " + PathName);
|
||||
@@ -129,10 +141,10 @@ namespace gaseous_server
|
||||
if (library.IsDefaultLibrary == false)
|
||||
{
|
||||
// check for active library scans
|
||||
foreach(ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
|
||||
{
|
||||
if (
|
||||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) ||
|
||||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScan && item.ItemState == ProcessQueue.QueueItemState.Running) ||
|
||||
(item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker && item.ItemState == ProcessQueue.QueueItemState.Running)
|
||||
)
|
||||
{
|
||||
@@ -174,7 +186,25 @@ namespace gaseous_server
|
||||
throw new LibraryNotFound(LibraryId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 LibraryItem(int Id, string Name, string Path, long DefaultPlatformId, bool IsDefaultLibrary)
|
||||
|
@@ -47,8 +47,11 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
public Dictionary<string, object> ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
|
||||
{
|
||||
Dictionary<string, object> RetVal = new Dictionary<string, object>();
|
||||
RetVal.Add("path", Path.GetFileName(GameFileImportPath));
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
@@ -67,8 +70,10 @@ namespace gaseous_server.Classes
|
||||
if (IsBios == null)
|
||||
{
|
||||
// file is a rom
|
||||
RetVal.Add("type", "rom");
|
||||
|
||||
// check to make sure we don't already have this file imported
|
||||
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
|
||||
sql = "SELECT COUNT(Id) AS count FROM view_Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
|
||||
dbDict.Add("md5", hash.md5hash);
|
||||
dbDict.Add("sha1", hash.sha1hash);
|
||||
DataTable importDB = db.ExecuteCMD(sql, dbDict);
|
||||
@@ -94,6 +99,8 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping import");
|
||||
}
|
||||
|
||||
RetVal.Add("status", "duplicate");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -122,32 +129,53 @@ namespace gaseous_server.Classes
|
||||
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature, discoveredSignature.Flags.IGDBPlatformId, true);
|
||||
|
||||
// add to database
|
||||
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
|
||||
long RomId = StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath, 0, true);
|
||||
|
||||
// build return value
|
||||
RetVal.Add("romid", RomId);
|
||||
RetVal.Add("platform", determinedPlatform);
|
||||
RetVal.Add("game", determinedGame);
|
||||
RetVal.Add("signature", discoveredSignature);
|
||||
RetVal.Add("status", "imported");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// file is a bios
|
||||
if (IsBios.WebEmulator != null)
|
||||
RetVal.Add("type", "bios");
|
||||
RetVal.Add("status", "notimported");
|
||||
|
||||
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
|
||||
{
|
||||
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
|
||||
if (biosItem.Available == false)
|
||||
{
|
||||
if (biosItem.Available == false && biosItem.hash == hash.md5hash)
|
||||
if (biosItem.hash == hash.md5hash)
|
||||
{
|
||||
string biosPath = biosItem.biosPath.Replace(biosItem.filename, "");
|
||||
if (!Directory.Exists(biosPath))
|
||||
{
|
||||
Directory.CreateDirectory(biosPath);
|
||||
}
|
||||
string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, biosItem.hash + ".bios");
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " is a BIOS file - moving to " + biosPath);
|
||||
|
||||
File.Move(GameFileImportPath, biosItem.biosPath, true);
|
||||
|
||||
break;
|
||||
RetVal.Add("name", biosItem.filename);
|
||||
RetVal.Add("platform", Platforms.GetPlatform(biosItem.platformid, false, false));
|
||||
RetVal["status"] = "imported";
|
||||
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (biosItem.hash == hash.md5hash)
|
||||
{
|
||||
RetVal["status"] = "duplicate";
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
|
||||
@@ -298,7 +326,7 @@ namespace gaseous_server.Classes
|
||||
return SearchCandidates;
|
||||
}
|
||||
|
||||
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, gaseous_server.Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
|
||||
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, gaseous_server.Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0, bool SourceIsExternal = false)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
@@ -308,7 +336,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
if (UpdateId == 0)
|
||||
{
|
||||
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, RelativePath, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -347,7 +375,13 @@ namespace gaseous_server.Classes
|
||||
dbDict.Add("romtype", (int)discoveredSignature.Rom.RomType);
|
||||
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(discoveredSignature.Rom.RomTypeMedia, ""));
|
||||
dbDict.Add("medialabel", Common.ReturnValueIfNull(discoveredSignature.Rom.MediaLabel, ""));
|
||||
dbDict.Add("path", GameFileImportPath);
|
||||
|
||||
string libraryRootPath = library.Path;
|
||||
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
|
||||
{
|
||||
libraryRootPath += Path.DirectorySeparatorChar;
|
||||
}
|
||||
dbDict.Add("path", GameFileImportPath.Replace(libraryRootPath, ""));
|
||||
|
||||
DataTable romInsert = db.ExecuteCMD(sql, dbDict);
|
||||
long romId = 0;
|
||||
@@ -363,7 +397,7 @@ namespace gaseous_server.Classes
|
||||
// move to destination
|
||||
if (library.IsDefaultLibrary == true)
|
||||
{
|
||||
MoveGameFile(romId);
|
||||
MoveGameFile(romId, SourceIsExternal);
|
||||
}
|
||||
|
||||
return romId;
|
||||
@@ -399,10 +433,14 @@ namespace gaseous_server.Classes
|
||||
return DestinationPathName;
|
||||
}
|
||||
|
||||
public static bool MoveGameFile(long RomId)
|
||||
public static bool MoveGameFile(long RomId, bool SourceIsExternal)
|
||||
{
|
||||
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
|
||||
string romPath = rom.Path;
|
||||
if (SourceIsExternal == true)
|
||||
{
|
||||
romPath = rom.RelativePath;
|
||||
}
|
||||
|
||||
if (File.Exists(romPath))
|
||||
{
|
||||
@@ -427,10 +465,16 @@ namespace gaseous_server.Classes
|
||||
|
||||
// update the db
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
|
||||
string sql = "UPDATE Games_Roms SET RelativePath=@path WHERE Id=@id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", RomId);
|
||||
dbDict.Add("path", DestinationPath);
|
||||
|
||||
string libraryRootPath = rom.Library.Path;
|
||||
if (libraryRootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == false)
|
||||
{
|
||||
libraryRootPath += Path.DirectorySeparatorChar;
|
||||
}
|
||||
dbDict.Add("path", DestinationPath.Replace(libraryRootPath, ""));
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return true;
|
||||
@@ -452,7 +496,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
// move rom files to their new location
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid";
|
||||
string sql = "SELECT * FROM view_Games_Roms WHERE LibraryId = @libraryid";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("libraryid", library.Id);
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
@@ -464,7 +508,7 @@ namespace gaseous_server.Classes
|
||||
SetStatus(i, romDT.Rows.Count, "Processing file " + romDT.Rows[i]["name"]);
|
||||
Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
|
||||
long RomId = (long)romDT.Rows[i]["id"];
|
||||
MoveGameFile(RomId);
|
||||
MoveGameFile(RomId, false);
|
||||
}
|
||||
}
|
||||
ClearStatus();
|
||||
@@ -492,61 +536,82 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public void LibraryScan(GameLibrary.LibraryItem? singleLibrary = null)
|
||||
public static List<GameLibrary.LibraryItem> LibrariesToScan = new List<GameLibrary.LibraryItem>();
|
||||
public void LibraryScan()
|
||||
{
|
||||
int maxWorkers = Config.MetadataConfiguration.MaxLibraryScanWorkers;
|
||||
|
||||
List<GameLibrary.LibraryItem> libraries = new List<GameLibrary.LibraryItem>();
|
||||
if (singleLibrary == null)
|
||||
if (LibrariesToScan.Count == 0)
|
||||
{
|
||||
libraries.AddRange(GameLibrary.GetLibraries);
|
||||
}
|
||||
else
|
||||
{
|
||||
libraries.Add(singleLibrary);
|
||||
LibrariesToScan.AddRange(GameLibrary.GetLibraries);
|
||||
}
|
||||
|
||||
// setup background tasks for each library
|
||||
foreach (GameLibrary.LibraryItem library in libraries)
|
||||
do
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting worker process for library " + library.Name);
|
||||
ProcessQueue.QueueItem queue = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.LibraryScanWorker,
|
||||
1,
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
},
|
||||
false,
|
||||
true);
|
||||
queue.Options = library;
|
||||
queue.ForceExecute();
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan queue size: " + LibrariesToScan.Count);
|
||||
|
||||
ProcessQueue.QueueItems.Add(queue);
|
||||
GameLibrary.LibraryItem library = LibrariesToScan[0];
|
||||
LibrariesToScan.RemoveAt(0);
|
||||
|
||||
// check number of running tasks is less than maxWorkers
|
||||
bool allowContinue;
|
||||
do
|
||||
// check if library is already being scanned
|
||||
bool libraryAlreadyScanning = false;
|
||||
List<ProcessQueue.QueueItem> ProcessQueueItems = new List<ProcessQueue.QueueItem>();
|
||||
ProcessQueueItems.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem item in ProcessQueueItems)
|
||||
{
|
||||
allowContinue = true;
|
||||
int currentWorkerCount = 0;
|
||||
List<ProcessQueue.QueueItem> queueItems = new List<ProcessQueue.QueueItem>();
|
||||
queueItems.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem item in queueItems)
|
||||
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
|
||||
{
|
||||
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
|
||||
if (((GameLibrary.LibraryItem)item.Options).Id == library.Id)
|
||||
{
|
||||
currentWorkerCount += 1;
|
||||
libraryAlreadyScanning = true;
|
||||
}
|
||||
}
|
||||
if (currentWorkerCount >= maxWorkers)
|
||||
}
|
||||
|
||||
if (libraryAlreadyScanning == false)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting worker process for library " + library.Name);
|
||||
ProcessQueue.QueueItem queue = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.LibraryScanWorker,
|
||||
1,
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.OrganiseLibrary,
|
||||
ProcessQueue.QueueItemType.Rematcher
|
||||
},
|
||||
false,
|
||||
true)
|
||||
{
|
||||
allowContinue = false;
|
||||
Thread.Sleep(60000);
|
||||
}
|
||||
} while (allowContinue == false);
|
||||
}
|
||||
Options = library
|
||||
};
|
||||
queue.ForceExecute();
|
||||
|
||||
ProcessQueue.QueueItems.Add(queue);
|
||||
|
||||
// check number of running tasks is less than maxWorkers
|
||||
bool allowContinue;
|
||||
do
|
||||
{
|
||||
allowContinue = true;
|
||||
int currentWorkerCount = 0;
|
||||
List<ProcessQueue.QueueItem> LibraryScan_QueueItems = new List<ProcessQueue.QueueItem>();
|
||||
LibraryScan_QueueItems.AddRange(ProcessQueue.QueueItems);
|
||||
foreach (ProcessQueue.QueueItem item in LibraryScan_QueueItems)
|
||||
{
|
||||
if (item.ItemType == ProcessQueue.QueueItemType.LibraryScanWorker)
|
||||
{
|
||||
currentWorkerCount += 1;
|
||||
}
|
||||
}
|
||||
if (currentWorkerCount >= maxWorkers)
|
||||
{
|
||||
allowContinue = false;
|
||||
Thread.Sleep(60000);
|
||||
}
|
||||
} while (allowContinue == false);
|
||||
}
|
||||
} while (LibrariesToScan.Count > 0);
|
||||
|
||||
bool WorkersStillWorking;
|
||||
do
|
||||
@@ -566,6 +631,12 @@ namespace gaseous_server.Classes
|
||||
} while (WorkersStillWorking == true);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan complete. All workers stopped");
|
||||
|
||||
if (LibrariesToScan.Count > 0)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "There are still libraries to scan. Restarting scan process");
|
||||
LibraryScan();
|
||||
}
|
||||
}
|
||||
|
||||
public void LibrarySpecificScan(GameLibrary.LibraryItem library)
|
||||
@@ -581,7 +652,7 @@ namespace gaseous_server.Classes
|
||||
dupDict.Add("libraryid", library.Id);
|
||||
db.ExecuteCMD(duplicateSql, dupDict);
|
||||
|
||||
string sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
string sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("libraryid", library.Id);
|
||||
DataTable dtRoms = db.ExecuteCMD(sql, dbDict);
|
||||
@@ -606,7 +677,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
dtRoms = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
// search for files in the library that aren't in the database
|
||||
@@ -678,7 +749,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
ClearStatus();
|
||||
|
||||
sql = "SELECT * FROM Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
sql = "SELECT * FROM view_Games_Roms WHERE LibraryId=@libraryid ORDER BY `name`";
|
||||
dtRoms = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
// check all roms to see if their local file still exists
|
||||
@@ -702,7 +773,7 @@ namespace gaseous_server.Classes
|
||||
if (romPath != ComputeROMPath(romId))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
|
||||
MoveGameFile(romId);
|
||||
MoveGameFile(romId, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -743,11 +814,11 @@ namespace gaseous_server.Classes
|
||||
string sql = "";
|
||||
if (ForceExecute == false)
|
||||
{
|
||||
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) AND LibraryId = @libraryid LIMIT 100;";
|
||||
sql = "SELECT * FROM view_Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) AND LibraryId = @libraryid LIMIT 100;";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;";
|
||||
sql = "SELECT * FROM view_Games_Roms WHERE (PlatformId = 0 AND GameId <> 0) OR (((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) OR (PlatformId = 0 AND GameId = 0)) AND LibraryId = @libraryid;";
|
||||
}
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
|
||||
|
@@ -3,10 +3,11 @@ using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Logging
|
||||
{
|
||||
public class Logging
|
||||
{
|
||||
private static DateTime lastDiskRetentionSweep = DateTime.UtcNow;
|
||||
public static bool WriteToDiskOnly { get; set; } = false;
|
||||
|
||||
@@ -21,8 +22,13 @@ namespace gaseous_server.Classes
|
||||
ExceptionValue = Common.ReturnValueIfNull(ExceptionValue, "").ToString()
|
||||
};
|
||||
|
||||
_ = Task.Run(() => WriteLogAsync(logItem, LogToDiskOnly));
|
||||
}
|
||||
|
||||
static async Task WriteLogAsync(LogItem logItem, bool LogToDiskOnly)
|
||||
{
|
||||
bool AllowWrite = false;
|
||||
if (EventType == LogType.Debug)
|
||||
if (logItem.EventType == LogType.Debug)
|
||||
{
|
||||
if (Config.LoggingConfiguration.DebugLogging == true)
|
||||
{
|
||||
@@ -42,26 +48,32 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
TraceOutput += Environment.NewLine + logItem.ExceptionValue.ToString();
|
||||
}
|
||||
switch(logItem.EventType) {
|
||||
string consoleColour = "";
|
||||
switch (logItem.EventType)
|
||||
{
|
||||
case LogType.Information:
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
// Console.ForegroundColor = ConsoleColor.Blue;
|
||||
consoleColour = "\u001b[1;34m]";
|
||||
break;
|
||||
|
||||
case LogType.Warning:
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
// Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
consoleColour = "\u001b[1;33m]";
|
||||
break;
|
||||
|
||||
case LogType.Critical:
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
// Console.ForegroundColor = ConsoleColor.Red;
|
||||
consoleColour = "\u001b[1;31m]";
|
||||
break;
|
||||
|
||||
case LogType.Debug:
|
||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||
// Console.ForegroundColor = ConsoleColor.Magenta;
|
||||
consoleColour = "\u001b[1;36m]";
|
||||
break;
|
||||
|
||||
}
|
||||
Console.WriteLine(TraceOutput);
|
||||
Console.ResetColor();
|
||||
Console.WriteLine(consoleColour + TraceOutput);
|
||||
// Console.ResetColor();
|
||||
|
||||
if (WriteToDiskOnly == true)
|
||||
{
|
||||
@@ -143,7 +155,7 @@ namespace gaseous_server.Classes
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
{
|
||||
LogToDisk(logItem, TraceOutput, ex);
|
||||
}
|
||||
}
|
||||
@@ -162,9 +174,9 @@ namespace gaseous_server.Classes
|
||||
foreach (string file in files)
|
||||
{
|
||||
FileInfo fi = new FileInfo(file);
|
||||
if (fi.LastAccessTime < DateTime.Now.AddDays(Config.LoggingConfiguration.LogRetention * -1))
|
||||
{
|
||||
fi.Delete();
|
||||
if (fi.LastAccessTime < DateTime.Now.AddDays(Config.LoggingConfiguration.LogRetention * -1))
|
||||
{
|
||||
fi.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +188,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
// dump the error
|
||||
File.AppendAllText(Config.LogFilePath, logItem.EventTime.ToString("yyyyMMdd HHmmss") + ": " + logItem.EventType.ToString() + ": " + logItem.Process + ": " + logItem.Message + Environment.NewLine + exception.ToString());
|
||||
|
||||
|
||||
|
||||
// something went wrong writing to the db
|
||||
File.AppendAllText(Config.LogFilePath, logItem.EventTime.ToString("yyyyMMdd HHmmss") + ": The following event was unable to be written to the log database:");
|
||||
@@ -185,7 +197,7 @@ namespace gaseous_server.Classes
|
||||
File.AppendAllText(Config.LogFilePath, TraceOutput);
|
||||
}
|
||||
|
||||
static public List<LogItem> GetLogs(LogsViewModel model)
|
||||
static public List<LogItem> GetLogs(LogsViewModel model)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
@@ -278,7 +290,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
whereClause = "WHERE " + whereClause;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -287,7 +299,7 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
whereClause = "AND " + whereClause;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Web.CodeGeneration;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class Maintenance : QueueItemStatus
|
||||
public class Maintenance : QueueItemStatus
|
||||
{
|
||||
const int MaxFileAge = 30;
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace gaseous_server.Classes
|
||||
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)
|
||||
{
|
||||
@@ -33,9 +34,29 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
|
||||
// delete old logs
|
||||
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRetentionDate;";
|
||||
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));
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
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
|
||||
List<string> PathsToClean = new List<string>();
|
||||
@@ -87,7 +108,7 @@ namespace gaseous_server.Classes
|
||||
SetStatus(StatusCounter, tables.Rows.Count, "Optimising 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)
|
||||
{
|
||||
string retVal = "";
|
||||
|
@@ -46,7 +46,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
default:
|
||||
throw new Exception("How did you get here?");
|
||||
}
|
||||
|
||||
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
// 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;
|
||||
@@ -86,7 +86,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
ageGroup.AgeGroupId = highestAgeGroup;
|
||||
}
|
||||
|
||||
|
||||
return ageGroup;
|
||||
}
|
||||
else
|
||||
@@ -95,7 +95,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
ageGroup.Id = game.Id;
|
||||
ageGroup.GameId = game.Id;
|
||||
ageGroup.AgeGroupId = null;
|
||||
|
||||
|
||||
return ageGroup;
|
||||
}
|
||||
}
|
||||
@@ -105,11 +105,11 @@ namespace gaseous_server.Classes.Metadata
|
||||
ageGroup.Id = game.Id;
|
||||
ageGroup.GameId = game.Id;
|
||||
ageGroup.AgeGroupId = null;
|
||||
|
||||
|
||||
return ageGroup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
foreach (KeyValuePair<AgeRestrictionGroupings, AgeGroupItem> ageGroupItem in AgeGroupingsFlat)
|
||||
{
|
||||
|
||||
|
||||
PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties();
|
||||
foreach (PropertyInfo property in groupProps)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return highestAgeGroup;
|
||||
}
|
||||
|
||||
@@ -158,8 +158,8 @@ namespace gaseous_server.Classes.Metadata
|
||||
get
|
||||
{
|
||||
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
|
||||
{
|
||||
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
|
||||
{
|
||||
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
|
||||
},
|
||||
{
|
||||
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
|
||||
@@ -167,7 +167,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
|
||||
},
|
||||
{
|
||||
{
|
||||
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
|
||||
}
|
||||
};
|
||||
@@ -216,44 +216,48 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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 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}
|
||||
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
|
||||
|
@@ -5,7 +5,7 @@ using IGDB.Models;
|
||||
|
||||
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;";
|
||||
|
||||
@@ -68,13 +68,19 @@ namespace gaseous_server.Classes.Metadata
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Artwork oldImage = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -120,6 +126,6 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -191,10 +191,11 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
if (InRateLimitAvoidanceMode == true)
|
||||
{
|
||||
// sleep for a moment to help avoid hitting the rate limiter
|
||||
Logging.Log(Logging.LogType.Information, "API Connection: Endpoint:" + Endpoint, "IGDB rate limit hit. Pausing API communications for " + RateLimitAvoidanceWait + " milliseconds to avoid rate limiter.");
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
}
|
||||
|
||||
@@ -203,7 +204,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
// increment rate limiter avoidance call count
|
||||
RateLimitAvoidanceCallCount += 1;
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
catch (ApiException apiEx)
|
||||
@@ -219,15 +220,15 @@ namespace gaseous_server.Classes.Metadata
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB API rate limit hit while accessing endpoint " + Endpoint, apiEx);
|
||||
|
||||
|
||||
RetryAttempts += 1;
|
||||
|
||||
return await IGDBAPI<T>(Endpoint, Fields, Query);
|
||||
}
|
||||
|
||||
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logging.Log(Logging.LogType.Information, "API Connection", "IGDB API unauthorised error while accessing endpoint " + Endpoint + ". Waiting " + RateLimitAvoidanceWait + " milliseconds and resetting IGDB client.", apiEx);
|
||||
|
||||
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
|
||||
igdb = new IGDBClient(
|
||||
@@ -245,7 +246,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "API Connection", "Exception when accessing endpoint " + Endpoint, ex);
|
||||
throw;
|
||||
@@ -260,7 +261,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
public Task<bool?> DownloadFile(Uri uri, string DestinationFile)
|
||||
{
|
||||
var result = _DownloadFile(uri, DestinationFile);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -355,7 +356,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case IGDBAPI_ImageSize.screenshot_small:
|
||||
@@ -363,7 +364,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
width = 235;
|
||||
height = 128;
|
||||
break;
|
||||
|
||||
|
||||
case IGDBAPI_ImageSize.screenshot_thumb:
|
||||
// 165x90
|
||||
width = 165;
|
||||
@@ -393,6 +394,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
if (InRateLimitAvoidanceMode == true)
|
||||
{
|
||||
// sleep for a moment to help avoid hitting the rate limiter
|
||||
Logging.Log(Logging.LogType.Information, "API Connection: Fetch Image", "IGDB rate limit hit. Pausing API communications for " + RateLimitAvoidanceWait + " milliseconds to avoid rate limiter.");
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
}
|
||||
|
||||
@@ -412,7 +414,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
await comms.IGDBAPI_GetImage(imageSizes, ImageId, ImagePath);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
@@ -497,14 +499,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
public async Task IGDBAPI_GetImage(List<IGDBAPI_ImageSize> ImageSizes, string ImageId, string OutputPath)
|
||||
{
|
||||
string urlTemplate = "https://images.igdb.com/igdb/image/upload/t_{size}/{hash}.jpg";
|
||||
|
||||
|
||||
foreach (IGDBAPI_ImageSize ImageSize in ImageSizes)
|
||||
{
|
||||
string url = urlTemplate.Replace("{size}", Common.GetDescription(ImageSize)).Replace("{hash}", ImageId);
|
||||
string newOutputPath = Path.Combine(OutputPath, Common.GetDescription(ImageSize));
|
||||
string OutputFile = ImageId + ".jpg";
|
||||
string fullPath = Path.Combine(newOutputPath, OutputFile);
|
||||
|
||||
|
||||
await _DownloadFile(new Uri(url), fullPath);
|
||||
}
|
||||
}
|
||||
|
@@ -76,7 +76,13 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
CompanyLogo oldImage = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Elfie.Model.Strings;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Covers
|
||||
public class Covers
|
||||
{
|
||||
const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
|
||||
|
||||
@@ -70,13 +70,19 @@ namespace gaseous_server.Classes.Metadata
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Cover oldCover = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
|
||||
if (oldCover.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -135,6 +141,6 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using gaseous_server.Models;
|
||||
using IGDB;
|
||||
using IGDB.Models;
|
||||
|
||||
@@ -7,7 +8,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Games
|
||||
{
|
||||
const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collection,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;";
|
||||
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()
|
||||
{
|
||||
@@ -127,17 +128,17 @@ namespace gaseous_server.Classes.Metadata
|
||||
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
|
||||
{
|
||||
// required metadata
|
||||
if (Game.Cover != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
|
||||
}
|
||||
}
|
||||
// if (Game.Cover != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (Game.Genres != null)
|
||||
{
|
||||
@@ -504,24 +505,161 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<long, string>> GetAvailablePlatforms(long GameId)
|
||||
public static List<AvailablePlatformItem> GetAvailablePlatforms(string UserId, long GameId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @gameid ORDER BY Platform.`Name`;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("gameid", GameId);
|
||||
string sql = @"
|
||||
SELECT DISTINCT
|
||||
view_Games_Roms.GameId,
|
||||
view_Games_Roms.PlatformId,
|
||||
Platform.`Name`,
|
||||
User_RecentPlayedRoms.UserId AS MostRecentUserId,
|
||||
User_RecentPlayedRoms.RomId AS MostRecentRomId,
|
||||
CASE User_RecentPlayedRoms.IsMediaGroup
|
||||
WHEN 0 THEN GMR.`Name`
|
||||
WHEN 1 THEN 'Media Group'
|
||||
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 'Media Group'
|
||||
ELSE NULL
|
||||
END AS `FavouriteRomName`,
|
||||
User_GameFavouriteRoms.IsMediaGroup AS FavouriteRomIsMediaGroup
|
||||
FROM
|
||||
view_Games_Roms
|
||||
LEFT JOIN
|
||||
Platform ON view_Games_Roms.PlatformId = Platform.Id
|
||||
LEFT JOIN
|
||||
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
|
||||
AND User_RecentPlayedRoms.GameId = view_Games_Roms.GameId
|
||||
AND User_RecentPlayedRoms.PlatformId = view_Games_Roms.PlatformId
|
||||
LEFT JOIN
|
||||
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||
AND User_GameFavouriteRoms.GameId = view_Games_Roms.GameId
|
||||
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.GameId = @gameid
|
||||
ORDER BY Platform.`Name`;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", UserId }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<KeyValuePair<long, string>> platforms = new List<KeyValuePair<long, string>>();
|
||||
PlatformMapping platformMapping = new PlatformMapping();
|
||||
List<AvailablePlatformItem> platforms = new List<AvailablePlatformItem>();
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
KeyValuePair<long, string> valuePair = new KeyValuePair<long, string>((long)row["PlatformId"], (string)row["Name"]);
|
||||
IGDB.Models.Platform platform = Platforms.GetPlatform((long)row["PlatformId"]);
|
||||
PlatformMapping.UserEmulatorConfiguration? emulatorConfiguration = platformMapping.GetUserEmulator(UserId, GameId, (long)platform.Id);
|
||||
|
||||
if (emulatorConfiguration == null)
|
||||
{
|
||||
if (platform.Id != 0)
|
||||
{
|
||||
Models.PlatformMapping.PlatformMapItem platformMap = PlatformMapping.GetPlatformMap((long)platform.Id);
|
||||
emulatorConfiguration = new PlatformMapping.UserEmulatorConfiguration
|
||||
{
|
||||
EmulatorType = platformMap.WebEmulator.Type,
|
||||
Core = platformMap.WebEmulator.Core,
|
||||
EnableBIOSFiles = platformMap.EnabledBIOSHashes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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["MostRecentRomName"] != System.DBNull.Value)
|
||||
{
|
||||
FavouriteRomName = string.IsNullOrEmpty((string?)row["MostRecentRomName"]) ? "" : (string)row["MostRecentRomName"];
|
||||
}
|
||||
}
|
||||
|
||||
AvailablePlatformItem valuePair = new AvailablePlatformItem
|
||||
{
|
||||
Id = platform.Id,
|
||||
Name = platform.Name,
|
||||
Category = platform.Category,
|
||||
emulatorConfiguration = emulatorConfiguration,
|
||||
LastPlayedRomId = LastPlayedRomId,
|
||||
LastPlayedRomIsMediagroup = LastPlayedIsMediagroup,
|
||||
LastPlayedRomName = LastPlayedRomName,
|
||||
FavouriteRomId = FavouriteRomId,
|
||||
FavouriteRomIsMediagroup = FavouriteRomIsMediagroup,
|
||||
FavouriteRomName = FavouriteRomName
|
||||
};
|
||||
platforms.Add(valuePair);
|
||||
}
|
||||
|
||||
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 : IGDB.Models.Platform
|
||||
{
|
||||
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 enum SearchType
|
||||
{
|
||||
where = 0,
|
||||
@@ -542,6 +680,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
this.Id = gameObject.Id;
|
||||
this.Name = gameObject.Name;
|
||||
this.Slug = gameObject.Slug;
|
||||
this.Summary = gameObject.Summary;
|
||||
this.TotalRating = gameObject.TotalRating;
|
||||
this.TotalRatingCount = gameObject.TotalRatingCount;
|
||||
this.Cover = gameObject.Cover;
|
||||
@@ -567,6 +706,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
public long Index { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public double? TotalRating { get; set; }
|
||||
public int? TotalRatingCount { get; set; }
|
||||
public bool HasSavedGame { get; set; } = false;
|
||||
|
@@ -76,7 +76,13 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
PlatformLogo oldImage = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -5,15 +5,15 @@ using IGDB.Models;
|
||||
|
||||
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 PlatformVersions()
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform)
|
||||
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform, bool GetImages = false)
|
||||
{
|
||||
if (Id == 0)
|
||||
{
|
||||
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
else
|
||||
{
|
||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform);
|
||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform)
|
||||
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform, bool GetImages = false)
|
||||
{
|
||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform);
|
||||
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform)
|
||||
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform, bool GetImages)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -67,7 +67,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
if (returnValue != null)
|
||||
{
|
||||
Storage.NewCacheValue(returnValue);
|
||||
UpdateSubClasses(ParentPlatform, returnValue);
|
||||
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
|
||||
}
|
||||
return returnValue;
|
||||
case Storage.CacheStatus.Expired:
|
||||
@@ -75,7 +75,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
UpdateSubClasses(ParentPlatform, returnValue);
|
||||
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -90,17 +90,20 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
|
||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion, bool GetImages)
|
||||
{
|
||||
if (platformVersion.PlatformLogo != null)
|
||||
if (GetImages == true)
|
||||
{
|
||||
try
|
||||
if (platformVersion.PlatformLogo != null)
|
||||
{
|
||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
|
||||
try
|
||||
{
|
||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
}
|
||||
|
||||
public static Platform? GetPlatform(long Id, bool forceRefresh = false)
|
||||
public static Platform? GetPlatform(long Id, bool forceRefresh = false, bool GetImages = false)
|
||||
{
|
||||
if (Id == 0)
|
||||
{
|
||||
@@ -41,7 +41,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
try
|
||||
{
|
||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
|
||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -52,13 +52,13 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
|
||||
public static Platform GetPlatform(string Slug, bool forceRefresh = false, bool GetImages = false)
|
||||
{
|
||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh);
|
||||
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh, GetImages);
|
||||
return RetVal.Result;
|
||||
}
|
||||
|
||||
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh)
|
||||
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh, bool GetImages)
|
||||
{
|
||||
// check database first
|
||||
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
|
||||
@@ -99,7 +99,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
returnValue = await GetObjectFromServer(WhereClause);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
UpdateSubClasses(returnValue);
|
||||
UpdateSubClasses(returnValue, GetImages);
|
||||
AddPlatformMapping(returnValue);
|
||||
return returnValue;
|
||||
case Storage.CacheStatus.Expired:
|
||||
@@ -107,7 +107,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
UpdateSubClasses(returnValue);
|
||||
UpdateSubClasses(returnValue, GetImages);
|
||||
AddPlatformMapping(returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSubClasses(Platform platform)
|
||||
private static void UpdateSubClasses(Platform platform, bool GetImages)
|
||||
{
|
||||
if (platform.Versions != null)
|
||||
{
|
||||
@@ -133,15 +133,18 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.PlatformLogo != null)
|
||||
if (GetImages == true)
|
||||
{
|
||||
try
|
||||
if (platform.PlatformLogo != null)
|
||||
{
|
||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
|
||||
try
|
||||
{
|
||||
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ using IGDB.Models;
|
||||
|
||||
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;";
|
||||
|
||||
@@ -68,13 +68,19 @@ namespace gaseous_server.Classes.Metadata
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue);
|
||||
forceImageDownload = true;
|
||||
break;
|
||||
break;
|
||||
case Storage.CacheStatus.Expired:
|
||||
try
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
forceImageDownload = true;
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Screenshot oldImage = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -120,6 +126,6 @@ namespace gaseous_server.Classes.Metadata
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,19 +7,19 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
public class Storage
|
||||
{
|
||||
public enum CacheStatus
|
||||
{
|
||||
NotPresent,
|
||||
Current,
|
||||
Expired
|
||||
}
|
||||
public class Storage
|
||||
{
|
||||
public enum CacheStatus
|
||||
{
|
||||
NotPresent,
|
||||
Current,
|
||||
Expired
|
||||
}
|
||||
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
|
||||
{
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
|
||||
{
|
||||
return _GetCacheStatus(Endpoint, "slug", Slug);
|
||||
}
|
||||
}
|
||||
|
||||
public static CacheStatus GetCacheStatus(string Endpoint, long Id)
|
||||
{
|
||||
@@ -47,124 +47,124 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
|
||||
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
|
||||
{
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
||||
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
|
||||
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Endpoint", Endpoint);
|
||||
dbDict.Add(SearchField, SearchValue);
|
||||
|
||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
// no data stored for this item, or lastUpdated
|
||||
return CacheStatus.NotPresent;
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
|
||||
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
|
||||
{
|
||||
return CacheStatus.Expired;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CacheStatus.Current;
|
||||
}
|
||||
}
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Endpoint", Endpoint);
|
||||
dbDict.Add(SearchField, SearchValue);
|
||||
|
||||
DataTable dt = db.ExecuteCMD(sql, dbDict);
|
||||
if (dt.Rows.Count == 0)
|
||||
{
|
||||
// no data stored for this item, or lastUpdated
|
||||
return CacheStatus.NotPresent;
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
|
||||
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
|
||||
{
|
||||
return CacheStatus.Expired;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CacheStatus.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
|
||||
{
|
||||
// get the object type name
|
||||
string ObjectTypeName = ObjectToCache.GetType().Name;
|
||||
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
|
||||
{
|
||||
// get the object type name
|
||||
string ObjectTypeName = ObjectToCache.GetType().Name;
|
||||
|
||||
// build dictionary
|
||||
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
|
||||
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
|
||||
// build dictionary
|
||||
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
|
||||
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
|
||||
objectDict.Add("dateAdded", DateTime.UtcNow);
|
||||
objectDict.Add("lastUpdated", DateTime.UtcNow);
|
||||
|
||||
// generate sql
|
||||
string fieldList = "";
|
||||
string valueList = "";
|
||||
string updateFieldValueList = "";
|
||||
foreach (KeyValuePair<string, object?> key in objectDict)
|
||||
{
|
||||
if (fieldList.Length > 0)
|
||||
{
|
||||
fieldList = fieldList + ", ";
|
||||
valueList = valueList + ", ";
|
||||
}
|
||||
fieldList = fieldList + key.Key;
|
||||
valueList = valueList + "@" + key.Key;
|
||||
if ((key.Key != "id") && (key.Key != "dateAdded"))
|
||||
// generate sql
|
||||
string fieldList = "";
|
||||
string valueList = "";
|
||||
string updateFieldValueList = "";
|
||||
foreach (KeyValuePair<string, object?> key in objectDict)
|
||||
{
|
||||
if (fieldList.Length > 0)
|
||||
{
|
||||
fieldList = fieldList + ", ";
|
||||
valueList = valueList + ", ";
|
||||
}
|
||||
fieldList = fieldList + key.Key;
|
||||
valueList = valueList + "@" + key.Key;
|
||||
if ((key.Key != "id") && (key.Key != "dateAdded"))
|
||||
{
|
||||
if (updateFieldValueList.Length > 0)
|
||||
{
|
||||
updateFieldValueList = updateFieldValueList + ", ";
|
||||
}
|
||||
updateFieldValueList += key.Key + " = @" + key.Key;
|
||||
}
|
||||
}
|
||||
|
||||
// check property type
|
||||
Type objectType = ObjectToCache.GetType();
|
||||
if (objectType != null)
|
||||
{
|
||||
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
|
||||
if (objectProperty != null)
|
||||
{
|
||||
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
|
||||
var objectValue = objectProperty.GetValue(ObjectToCache);
|
||||
if (objectValue != null)
|
||||
{
|
||||
string newObjectValue;
|
||||
Dictionary<string, object> newDict;
|
||||
// check property type
|
||||
Type objectType = ObjectToCache.GetType();
|
||||
if (objectType != null)
|
||||
{
|
||||
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
|
||||
if (objectProperty != null)
|
||||
{
|
||||
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
|
||||
var objectValue = objectProperty.GetValue(ObjectToCache);
|
||||
if (objectValue != null)
|
||||
{
|
||||
string newObjectValue;
|
||||
Dictionary<string, object> newDict;
|
||||
switch (compareName)
|
||||
{
|
||||
case "identityorvalue":
|
||||
{
|
||||
case "identityorvalue":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
objectDict[key.Key] = newDict["Id"];
|
||||
break;
|
||||
case "identitiesorvalues":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
|
||||
objectDict[key.Key] = newObjectValue;
|
||||
|
||||
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
|
||||
|
||||
break;
|
||||
case "int32[]":
|
||||
case "int32[]":
|
||||
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
|
||||
objectDict[key.Key] = newObjectValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string sql = "";
|
||||
if (UpdateRecord == false)
|
||||
{
|
||||
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string sql = "";
|
||||
if (UpdateRecord == false)
|
||||
{
|
||||
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
|
||||
}
|
||||
|
||||
// execute sql
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
db.ExecuteCMD(sql, objectDict);
|
||||
db.ExecuteCMD(sql, objectDict);
|
||||
}
|
||||
|
||||
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
|
||||
{
|
||||
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
|
||||
{
|
||||
string Endpoint = EndpointType.GetType().Name;
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
@@ -175,23 +175,23 @@ namespace gaseous_server.Classes.Metadata
|
||||
dbDict.Add("Endpoint", Endpoint);
|
||||
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)
|
||||
{
|
||||
// no data stored for this item
|
||||
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
|
||||
// no data stored for this item
|
||||
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
DataRow dataRow = dt.Rows[0];
|
||||
DataRow dataRow = dt.Rows[0];
|
||||
object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
|
||||
|
||||
|
||||
return (T)returnObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
||||
{
|
||||
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
|
||||
{
|
||||
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
|
||||
{
|
||||
if (dataRow.Table.Columns.Contains(property.Name))
|
||||
@@ -428,11 +428,35 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
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 + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
|
||||
db.ExecuteCMD(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MemoryCacheObject
|
||||
{
|
||||
public object Object { get; set; }
|
||||
public DateTime CreationTime { get; } = DateTime.UtcNow;
|
||||
public DateTime ExpiryTime
|
||||
public DateTime ExpiryTime
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@@ -11,12 +11,12 @@ using gaseous_server.Models;
|
||||
|
||||
namespace gaseous_server.Classes
|
||||
{
|
||||
public class RomMediaGroup
|
||||
public class RomMediaGroup
|
||||
{
|
||||
public class InvalidMediaGroupId : Exception
|
||||
{
|
||||
{
|
||||
public InvalidMediaGroupId(long Id) : base("Unable to find media group by id " + Id)
|
||||
{}
|
||||
{ }
|
||||
}
|
||||
|
||||
public static GameRomMediaGroupItem CreateMediaGroup(long GameId, long PlatformId, List<long> RomIds)
|
||||
@@ -81,14 +81,42 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId, string userid = "")
|
||||
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);
|
||||
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.GameId=@gameid;";
|
||||
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>
|
||||
{
|
||||
{ "gameid", GameId },
|
||||
{ "userid", userid }
|
||||
{ "userid", userid },
|
||||
{ "platformid", PlatformId }
|
||||
};
|
||||
|
||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||
@@ -165,7 +193,7 @@ namespace gaseous_server.Classes
|
||||
public static void DeleteMediaGroup(long Id)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1;";
|
||||
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId=@id AND IsMediaGroup = 1;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", Id);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
@@ -180,22 +208,42 @@ namespace gaseous_server.Classes
|
||||
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
||||
{
|
||||
bool hasSaveStates = false;
|
||||
if (row.Table.Columns.Contains("GameStateRomId"))
|
||||
{
|
||||
if (row["GameStateRomId"] != DBNull.Value)
|
||||
{
|
||||
hasSaveStates = true;
|
||||
}
|
||||
}
|
||||
if (row.Table.Columns.Contains("GameStateRomId"))
|
||||
{
|
||||
if (row["GameStateRomId"] != DBNull.Value)
|
||||
{
|
||||
hasSaveStates = true;
|
||||
}
|
||||
}
|
||||
|
||||
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
|
||||
mediaGroupItem.Id = (long)row["Id"];
|
||||
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
|
||||
mediaGroupItem.PlatformId = (long)row["PlatformId"];
|
||||
mediaGroupItem.GameId = (long)row["GameId"];
|
||||
mediaGroupItem.RomIds = new List<long>();
|
||||
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
|
||||
mediaGroupItem.HasSaveStates = hasSaveStates;
|
||||
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
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
@@ -216,18 +264,6 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
// check for a web emulator and update the romItem
|
||||
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||
{
|
||||
if (platformMapping.IGDBId == mediaGroupItem.PlatformId)
|
||||
{
|
||||
if (platformMapping.WebEmulator != null)
|
||||
{
|
||||
mediaGroupItem.Emulator = platformMapping.WebEmulator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mediaGroupItem;
|
||||
}
|
||||
|
||||
@@ -301,7 +337,7 @@ namespace gaseous_server.Classes
|
||||
if (File.Exists(rom.Path))
|
||||
{
|
||||
string romExt = Path.GetExtension(rom.Path);
|
||||
if (new string[]{ ".zip", ".rar", ".7z" }.Contains(romExt))
|
||||
if (new string[] { ".zip", ".rar", ".7z" }.Contains(romExt))
|
||||
{
|
||||
Logging.Log(Logging.LogType.Information, "Media Group", "Decompressing ROM: " + rom.Name);
|
||||
|
||||
@@ -511,11 +547,12 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
|
||||
public class GameRomMediaGroupItem
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public long PlatformId { get; set; }
|
||||
public string Platform {
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public long PlatformId { get; set; }
|
||||
public string Platform
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
@@ -528,37 +565,40 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||
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;
|
||||
private GroupBuildStatus _Status { get; set; }
|
||||
public GroupBuildStatus Status {
|
||||
get
|
||||
{
|
||||
if (_Status == GroupBuildStatus.Completed)
|
||||
{
|
||||
if (File.Exists(MediaGroupZipPath))
|
||||
{
|
||||
return GroupBuildStatus.Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GroupBuildStatus.NoStatus;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _Status;
|
||||
}
|
||||
}
|
||||
public bool RomUserLastUsed { get; set; }
|
||||
public bool RomUserFavourite { get; set; }
|
||||
private GroupBuildStatus _Status { get; set; }
|
||||
public GroupBuildStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Status == GroupBuildStatus.Completed)
|
||||
{
|
||||
if (File.Exists(MediaGroupZipPath))
|
||||
{
|
||||
return GroupBuildStatus.Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GroupBuildStatus.NoStatus;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _Status;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
_Status = value;
|
||||
}
|
||||
}
|
||||
public long? Size {
|
||||
get
|
||||
}
|
||||
public long? Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status == GroupBuildStatus.Completed)
|
||||
{
|
||||
@@ -577,15 +617,15 @@ namespace gaseous_server.Classes
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal string MediaGroupZipPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, Id + ".zip");
|
||||
}
|
||||
}
|
||||
public enum GroupBuildStatus
|
||||
}
|
||||
internal string MediaGroupZipPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, Id + ".zip");
|
||||
}
|
||||
}
|
||||
public enum GroupBuildStatus
|
||||
{
|
||||
NoStatus = 0,
|
||||
WaitingForBuild = 1,
|
||||
@@ -593,6 +633,6 @@ namespace gaseous_server.Classes
|
||||
Completed = 3,
|
||||
Failed = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,6 +18,12 @@ namespace gaseous_server.Classes
|
||||
{ }
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -33,34 +39,54 @@ namespace gaseous_server.Classes
|
||||
string NameSearchWhere = "";
|
||||
if (NameSearch.Length > 0)
|
||||
{
|
||||
NameSearchWhere = " AND Games_Roms.`Name` LIKE @namesearch";
|
||||
NameSearchWhere = " AND view_Games_Roms.`Name` LIKE @namesearch";
|
||||
dbDict.Add("namesearch", '%' + NameSearch + '%');
|
||||
}
|
||||
|
||||
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 = view_Games_Roms.GameId
|
||||
AND User_RecentPlayedRoms.PlatformId = view_Games_Roms.PlatformId
|
||||
AND User_RecentPlayedRoms.RomId = view_Games_Roms.Id
|
||||
AND User_RecentPlayedRoms.IsMediaGroup = 0
|
||||
LEFT JOIN
|
||||
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||
AND User_GameFavouriteRoms.GameId = view_Games_Roms.GameId
|
||||
AND User_GameFavouriteRoms.PlatformId = view_Games_Roms.PlatformId
|
||||
AND User_GameFavouriteRoms.RomId = view_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`;";
|
||||
sqlPlatform = "SELECT DISTINCT view_Games_Roms.PlatformId, Platform.`Name` FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id WHERE GameId = @id ORDER BY Platform.`Name`;";
|
||||
|
||||
if (PlatformId == -1)
|
||||
{
|
||||
// data query
|
||||
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
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.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, view_Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
||||
sqlCount = "SELECT COUNT(view_Games_Roms.Id) AS RomCount FROM view_Games_Roms WHERE view_Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
// data query
|
||||
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
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.GameId = @id AND view_Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, view_Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||
sqlCount = "SELECT COUNT(view_Games_Roms.Id) AS RomCount FROM view_Games_Roms WHERE view_Games_Roms.GameId = @id AND view_Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||
|
||||
dbDict.Add("platformid", PlatformId);
|
||||
}
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict)[0];
|
||||
DataTable platformDT = db.ExecuteCMD(sqlPlatform, dbDict);
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks));
|
||||
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromMinutes(1).Ticks))[0];
|
||||
|
||||
if (romDT.Rows.Count > 0)
|
||||
{
|
||||
@@ -70,10 +96,9 @@ namespace gaseous_server.Classes
|
||||
int pageOffset = pageSize * (pageNumber - 1);
|
||||
for (int i = 0; i < romDT.Rows.Count; i++)
|
||||
{
|
||||
GameRomItem gameRomItem = BuildRom(romDT.Rows[i]);
|
||||
|
||||
if ((i >= pageOffset && i < pageOffset + pageSize) || pageSize == 0)
|
||||
{
|
||||
GameRomItem gameRomItem = BuildRom(romDT.Rows[i]);
|
||||
GameRoms.GameRomItems.Add(gameRomItem);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +114,7 @@ namespace gaseous_server.Classes
|
||||
public static GameRomItem GetRom(long RomId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.Id = @id";
|
||||
string sql = "SELECT view_Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON view_Games_Roms.GameId = Game.Id WHERE view_Games_Roms.Id = @id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", RomId);
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
@@ -106,6 +131,26 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static GameRomItem GetRom(string MD5)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT view_Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM view_Games_Roms LEFT JOIN Platform ON view_Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON view_Games_Roms.GameId = Game.Id 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)
|
||||
{
|
||||
// ensure metadata for platformid is present
|
||||
@@ -185,7 +230,7 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id;";
|
||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id; UPDATE UserTimeTracking SET PlatformId = NULL, IsMediaGroup = NULL, RomId = NULL WHERE RomId = @id AND IsMediaGroup = 0;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("id", RomId);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
@@ -225,6 +270,7 @@ namespace gaseous_server.Classes
|
||||
PlatformId = (long)romDR["platformid"],
|
||||
Platform = (string)romDR["platformname"],
|
||||
GameId = (long)romDR["gameid"],
|
||||
Game = (string)romDR["gamename"],
|
||||
Name = (string)romDR["name"],
|
||||
Size = (long)romDR["size"],
|
||||
Crc = ((string)romDR["crc"]).ToLower(),
|
||||
@@ -236,21 +282,28 @@ namespace gaseous_server.Classes
|
||||
RomTypeMedia = (string)romDR["romtypemedia"],
|
||||
MediaLabel = (string)romDR["medialabel"],
|
||||
Path = (string)romDR["path"],
|
||||
RelativePath = (string)romDR["relativepath"],
|
||||
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
|
||||
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
||||
HasSaveStates = hasSaveStates,
|
||||
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
||||
};
|
||||
|
||||
// check for a web emulator and update the romItem
|
||||
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||
romItem.RomUserLastUsed = false;
|
||||
if (romDR.Table.Columns.Contains("MostRecentRomId"))
|
||||
{
|
||||
if (platformMapping.IGDBId == romItem.PlatformId)
|
||||
if (romDR["MostRecentRomId"] != DBNull.Value)
|
||||
{
|
||||
if (platformMapping.WebEmulator != null)
|
||||
{
|
||||
romItem.Emulator = platformMapping.WebEmulator;
|
||||
}
|
||||
romItem.RomUserLastUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
romItem.RomUserFavourite = false;
|
||||
if (romDR.Table.Columns.Contains("FavouriteRomId"))
|
||||
{
|
||||
if (romDR["FavouriteRomId"] != DBNull.Value)
|
||||
{
|
||||
romItem.RomUserFavourite = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,12 +320,15 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
public long PlatformId { get; set; }
|
||||
public string Platform { get; set; }
|
||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public string Game { get; set; }
|
||||
public string? Path { get; set; }
|
||||
public string? RelativePath { get; set; }
|
||||
public string? SignatureSourceGameTitle { get; set; }
|
||||
public bool HasSaveStates { get; set; } = false;
|
||||
public GameLibrary.LibraryItem Library { get; set; }
|
||||
public bool RomUserLastUsed { get; set; }
|
||||
public bool RomUserFavourite { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Data;
|
||||
using gaseous_server.Models;
|
||||
using gaseous_signature_parser.models.RomSignatureObject;
|
||||
using static gaseous_server.Classes.Common;
|
||||
|
||||
@@ -81,6 +82,47 @@ namespace gaseous_server.Classes
|
||||
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 = "";
|
||||
|
@@ -5,29 +5,45 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public StatisticsModel RecordSession(Guid SessionId, long GameId, string UserId)
|
||||
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;
|
||||
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) VALUES (@gameid, @userid, @sessionid, @sessiontime, @sessionlength);";
|
||||
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 }
|
||||
{ "sessionlength", 1 },
|
||||
{ "platformid", PlatformId },
|
||||
{ "ismediagroup", IsMediaGroup },
|
||||
{ "romid", RomId }
|
||||
};
|
||||
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
|
||||
return new StatisticsModel
|
||||
{
|
||||
GameId = GameId,
|
||||
SessionId = SessionId,
|
||||
SessionStart = (DateTime)dbDict["sessiontime"],
|
||||
@@ -50,7 +66,8 @@ namespace gaseous_server.Classes
|
||||
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid AND SessionId = @sessionid;";
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
return new StatisticsModel
|
||||
{
|
||||
GameId = (long)data.Rows[0]["GameId"],
|
||||
SessionId = Guid.Parse(data.Rows[0]["SessionId"].ToString()),
|
||||
SessionStart = (DateTime)data.Rows[0]["SessionTime"],
|
||||
@@ -87,7 +104,8 @@ namespace gaseous_server.Classes
|
||||
sql = "SELECT * FROM UserTimeTracking WHERE GameId = @gameid AND UserId = @userid ORDER BY SessionTime DESC LIMIT 1;";
|
||||
data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return new StatisticsModel{
|
||||
return new StatisticsModel
|
||||
{
|
||||
GameId = GameId,
|
||||
SessionLength = TotalTime,
|
||||
SessionStart = (DateTime)data.Rows[0]["SessionTime"]
|
||||
|
209
gaseous-server/Classes/UserProfile.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System.Data;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using IGDB.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)
|
||||
{
|
||||
NowPlaying = new Models.UserProfile.NowPlayingItem
|
||||
{
|
||||
Game = Games.GetGame((long)nowPlayingData.Rows[0]["GameId"], false, false, false),
|
||||
Platform = Platforms.GetPlatform((long)nowPlayingData.Rows[0]["PlatformId"], false, false),
|
||||
Duration = Convert.ToInt64(nowPlayingData.Rows[0]["SessionLength"])
|
||||
};
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -99,7 +99,7 @@ namespace gaseous_server.Controllers
|
||||
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
|
||||
profile.SecurityProfile = user.SecurityProfile;
|
||||
profile.UserPreferences = user.UserPreferences;
|
||||
profile.Avatar = user.Avatar;
|
||||
profile.ProfileId = user.ProfileId;
|
||||
profile.Roles.Sort();
|
||||
|
||||
return Ok(profile);
|
||||
@@ -122,7 +122,7 @@ namespace gaseous_server.Controllers
|
||||
profile.SecurityProfile = user.SecurityProfile;
|
||||
profile.UserPreferences = user.UserPreferences;
|
||||
profile.Roles.Sort();
|
||||
|
||||
|
||||
string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";";
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(profileString);
|
||||
@@ -188,8 +188,8 @@ namespace gaseous_server.Controllers
|
||||
user.LockoutEnabled = rawUser.LockoutEnabled;
|
||||
user.LockoutEnd = rawUser.LockoutEnd;
|
||||
user.SecurityProfile = rawUser.SecurityProfile;
|
||||
user.Avatar = rawUser.Avatar;
|
||||
|
||||
user.ProfileId = rawUser.ProfileId;
|
||||
|
||||
// get roles
|
||||
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
|
||||
if (aUser != null)
|
||||
@@ -214,12 +214,16 @@ namespace gaseous_server.Controllers
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
ApplicationUser user = new ApplicationUser
|
||||
{
|
||||
UserName = model.UserName,
|
||||
{
|
||||
UserName = model.UserName,
|
||||
NormalizedUserName = model.UserName.ToUpper(),
|
||||
Email = model.Email,
|
||||
NormalizedEmail = model.Email.ToUpper()
|
||||
};
|
||||
if (await _userManager.FindByEmailAsync(model.Email) != null)
|
||||
{
|
||||
return NotFound("User already exists");
|
||||
}
|
||||
var result = await _userManager.CreateAsync(user, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
@@ -234,20 +238,37 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[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]
|
||||
[Route("Users/{UserId}")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public async Task<IActionResult> GetUser(string UserId)
|
||||
{
|
||||
ApplicationUser? rawUser = await _userManager.FindByIdAsync(UserId);
|
||||
|
||||
|
||||
if (rawUser != null)
|
||||
{
|
||||
UserViewModel user = new UserViewModel();
|
||||
@@ -256,7 +277,8 @@ namespace gaseous_server.Controllers
|
||||
user.LockoutEnabled = rawUser.LockoutEnabled;
|
||||
user.LockoutEnd = rawUser.LockoutEnd;
|
||||
user.SecurityProfile = rawUser.SecurityProfile;
|
||||
|
||||
user.ProfileId = rawUser.ProfileId;
|
||||
|
||||
// get roles
|
||||
IList<string> aUserRoles = await _userManager.GetRolesAsync(rawUser);
|
||||
user.Roles = aUserRoles.ToList();
|
||||
@@ -297,16 +319,16 @@ namespace gaseous_server.Controllers
|
||||
public async Task<IActionResult> SetUserRoles(string UserId, string RoleName)
|
||||
{
|
||||
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
|
||||
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
// get roles
|
||||
List<string> userRoles = (await _userManager.GetRolesAsync(user)).ToList();
|
||||
|
||||
|
||||
// delete all roles
|
||||
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);
|
||||
}
|
||||
@@ -331,7 +353,7 @@ namespace gaseous_server.Controllers
|
||||
await _userManager.AddToRoleAsync(user, RoleName);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
@@ -348,12 +370,12 @@ namespace gaseous_server.Controllers
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
|
||||
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
user.SecurityProfile = securityProfile;
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
@@ -413,7 +435,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
user.UserPreferences = model;
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,11 @@ using gaseous_server.Classes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using gaseous_server.Models;
|
||||
using IGDB.Models;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -17,6 +22,15 @@ namespace gaseous_server.Controllers
|
||||
[Authorize]
|
||||
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.1")]
|
||||
[HttpGet]
|
||||
@@ -43,24 +57,51 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpHead]
|
||||
[Route("zip/{PlatformId}")]
|
||||
[Route("zip/{PlatformId}/{GameId}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetBiosCompressed(long PlatformId)
|
||||
public async Task<ActionResult> GetBiosCompressedAsync(long PlatformId, long GameId = -1, bool filtered = false)
|
||||
{
|
||||
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();
|
||||
|
||||
using (FileStream zipFile = System.IO.File.Create(tempFile))
|
||||
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
|
||||
zipArchive.CreateEntryFromFile(Path.Combine(Config.LibraryConfiguration.LibraryFirmwareDirectory, hash + ".bios"), bios.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
using static gaseous_server.Models.PlatformMapping;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -30,7 +31,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
|
||||
public GamesController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager
|
||||
@@ -53,7 +54,7 @@ namespace gaseous_server.Controllers
|
||||
int minrating = -1,
|
||||
int maxrating = -1,
|
||||
bool sortdescending = false)
|
||||
{
|
||||
{
|
||||
|
||||
return Ok(GetGames(name, platform, genre, gamemode, playerperspective, theme, minrating, maxrating, "Adult", true, true, sortdescending));
|
||||
}
|
||||
@@ -104,7 +105,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
if (platform.Length > 0)
|
||||
{
|
||||
tempVal = "Games_Roms.PlatformId IN (";
|
||||
tempVal = "view_Games_Roms.PlatformId IN (";
|
||||
string[] platformClauseItems = platform.Split(",");
|
||||
for (int i = 0; i < platformClauseItems.Length; i++)
|
||||
{
|
||||
@@ -280,9 +281,7 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
//string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.* FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
|
||||
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
string sql = "SELECT DISTINCT view_Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM view_Games_Roms LEFT JOIN Game ON Game.Id = view_Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
|
||||
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
|
||||
|
||||
@@ -457,73 +456,6 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/artwork/{ArtworkId}/image/{size}")]
|
||||
[Route("{GameId}/artwork/{ArtworkId}/image/{size}/{ImageName}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameCoverImage(long GameId, long ArtworkId, Communications.IGDBAPI_ImageSize size, string ImageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
try
|
||||
{
|
||||
IGDB.Models.Artwork artworkObject = Artworks.GetArtwork(ArtworkId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
if (artworkObject != null) {
|
||||
//string coverFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork", size.ToString(), artworkObject.ImageId + ".jpg");
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Artwork");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, artworkObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath))
|
||||
{
|
||||
string filename = artworkObject.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
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();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
@@ -562,47 +494,126 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/cover/image/{size}")]
|
||||
[Route("{GameId}/cover/image/{size}/{imagename}")]
|
||||
[Route("{GameId}/{ImageType}/{ImageId}/image/{size}")]
|
||||
[Route("{GameId}/{ImageType}/{ImageId}/image/{size}/{imagename}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameCoverImage(long GameId, Communications.IGDBAPI_ImageSize size, string imagename = "")
|
||||
public async Task<ActionResult> GameImage(long GameId, MetadataImageType imageType, long ImageId, Communications.IGDBAPI_ImageSize size, string imagename = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject.Cover != null)
|
||||
string? imageId = null;
|
||||
string? imageTypePath = null;
|
||||
|
||||
switch (imageType)
|
||||
{
|
||||
if (gameObject.Cover.Id != null)
|
||||
{
|
||||
IGDB.Models.Cover cover = Classes.Metadata.Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Covers");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, cover.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath)) {
|
||||
string filename = cover.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
case MetadataImageType.cover:
|
||||
if (gameObject.Cover != null)
|
||||
{
|
||||
if (gameObject.Cover.Id != null)
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
IGDB.Models.Cover cover = Classes.Metadata.Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
imageId = cover.ImageId;
|
||||
imageTypePath = "Covers";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
break;
|
||||
|
||||
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
case MetadataImageType.screenshots:
|
||||
if (gameObject.Screenshots != null)
|
||||
{
|
||||
if (gameObject.Screenshots.Ids.Contains(ImageId))
|
||||
{
|
||||
IGDB.Models.Screenshot imageObject = Screenshots.GetScreenshot(ImageId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
return File(filedata, contentType);
|
||||
imageId = imageObject.ImageId;
|
||||
imageTypePath = "Screenshots";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
break;
|
||||
|
||||
case MetadataImageType.artwork:
|
||||
if (gameObject.Artworks != null)
|
||||
{
|
||||
if (gameObject.Artworks.Ids.Contains(ImageId))
|
||||
{
|
||||
IGDB.Models.Artwork imageObject = Artworks.GetArtwork(ImageId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
imageId = imageObject.ImageId;
|
||||
imageTypePath = "Artwork";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (imageId == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), imageTypePath);
|
||||
string imagePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), imageTypePath, size.ToString(), imageId + ".jpg");
|
||||
|
||||
if (!System.IO.File.Exists(imagePath))
|
||||
{
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), imageTypePath), 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(basePath, imageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
imagePath = ImgFetch.Result;
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(imagePath))
|
||||
{
|
||||
string filename = imageId + ".jpg";
|
||||
string filepath = imagePath;
|
||||
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");
|
||||
|
||||
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 NotFound();
|
||||
}
|
||||
catch
|
||||
@@ -611,6 +622,13 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public enum MetadataImageType
|
||||
{
|
||||
cover,
|
||||
screenshots,
|
||||
artwork
|
||||
}
|
||||
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
@@ -627,7 +645,7 @@ namespace gaseous_server.Controllers
|
||||
if (gameObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Favourites favourites = new Favourites();
|
||||
@@ -664,7 +682,7 @@ namespace gaseous_server.Controllers
|
||||
if (gameObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Favourites favourites = new Favourites();
|
||||
@@ -796,7 +814,8 @@ namespace gaseous_server.Controllers
|
||||
companyData.Add("company", company);
|
||||
|
||||
return Ok(companyData);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
@@ -857,17 +876,146 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/emulatorconfiguration/{PlatformId}")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(UserEmulatorConfiguration), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GetGameEmulator(long GameId, long PlatformId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||
|
||||
if (platformObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
PlatformMapping platformMapping = new PlatformMapping();
|
||||
UserEmulatorConfiguration platformMappingObject = platformMapping.GetUserEmulator(user.Id, GameId, PlatformId);
|
||||
|
||||
if (platformMappingObject != null)
|
||||
{
|
||||
return Ok(platformMappingObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Route("{GameId}/emulatorconfiguration/{PlatformId}")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> SetGameEmulator(long GameId, long PlatformId, UserEmulatorConfiguration configuration)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||
|
||||
if (platformObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
PlatformMapping platformMapping = new PlatformMapping();
|
||||
platformMapping.SetUserEmulator(user.Id, GameId, PlatformId, configuration);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpDelete]
|
||||
[Route("{GameId}/emulatorconfiguration/{PlatformId}")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> DeleteGameEmulator(long GameId, long PlatformId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (gameObject != null)
|
||||
{
|
||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||
|
||||
if (platformObject != null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
PlatformMapping platformMapping = new PlatformMapping();
|
||||
platformMapping.DeleteUserEmulator(user.Id, GameId, PlatformId);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/platforms")]
|
||||
[ProducesResponseType(typeof(List<KeyValuePair<long, string>>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(List<Games.AvailablePlatformItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GamePlatforms(long GameId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(Games.GetAvailablePlatforms(GameId));
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return Ok(Games.GetAvailablePlatforms(user.Id, GameId));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -895,7 +1043,7 @@ namespace gaseous_server.Controllers
|
||||
foreach (long icId in gameObject.ReleaseDates.Ids)
|
||||
{
|
||||
ReleaseDate releaseDate = Classes.Metadata.ReleaseDates.GetReleaseDates(icId);
|
||||
|
||||
|
||||
rdObjects.Add(releaseDate);
|
||||
}
|
||||
}
|
||||
@@ -923,7 +1071,7 @@ namespace gaseous_server.Controllers
|
||||
public async Task<ActionResult> GameRomAsync(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1, string NameSearch = "")
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
@@ -1025,6 +1173,67 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Route("{GameId}/roms/{RomId}/{PlatformId}/favourite")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameRomFavourite(long GameId, long RomId, long PlatformId, bool IsMediaGroup, bool favourite)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationUser? user = await _userManager.GetUserAsync(User);
|
||||
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
if (IsMediaGroup == false)
|
||||
{
|
||||
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
|
||||
if (rom.GameId == GameId)
|
||||
{
|
||||
if (favourite == true)
|
||||
{
|
||||
Classes.Metadata.Games.GameSetFavouriteRom(user.Id, GameId, PlatformId, RomId, IsMediaGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
Classes.Metadata.Games.GameClearFavouriteRom(user.Id, GameId, PlatformId);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomId, user.Id);
|
||||
if (rom.GameId == GameId)
|
||||
{
|
||||
if (favourite == true)
|
||||
{
|
||||
Classes.Metadata.Games.GameSetFavouriteRom(user.Id, GameId, PlatformId, RomId, IsMediaGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
Classes.Metadata.Games.GameClearFavouriteRom(user.Id, GameId, PlatformId);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
@@ -1113,7 +1322,7 @@ namespace gaseous_server.Controllers
|
||||
public async Task<ActionResult> GameRomGroupAsync(long GameId, long RomGroupId)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
@@ -1137,21 +1346,20 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[Route("{GameId}/romgroup")]
|
||||
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GetGameRomGroupAsync(long GameId)
|
||||
public async Task<ActionResult> GetGameRomGroupAsync(long GameId, long? PlatformId = null)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
try
|
||||
{
|
||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id));
|
||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id, PlatformId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1204,7 +1412,7 @@ namespace gaseous_server.Controllers
|
||||
public async Task<ActionResult> GameRomGroupMembersAsync(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
@@ -1386,9 +1594,10 @@ namespace gaseous_server.Controllers
|
||||
public async Task<ActionResult> GameScreenshot(long GameId, long ScreenshotId)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
if (gameObject != null) {
|
||||
if (gameObject != null)
|
||||
{
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
if (screenshotObject != null)
|
||||
{
|
||||
@@ -1410,56 +1619,56 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}")]
|
||||
[Route("{GameId}/screenshots/{ScreenshotId}/image/{size}/{ImageName}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GameScreenshotImage(long GameId, long ScreenshotId, Communications.IGDBAPI_ImageSize Size, string ImageName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
// [MapToApiVersion("1.0")]
|
||||
// [MapToApiVersion("1.1")]
|
||||
// [HttpGet]
|
||||
// [Route("{GameId}/screenshots/{ScreenshotId}/image/{size}")]
|
||||
// [Route("{GameId}/screenshots/{ScreenshotId}/image/{size}/{ImageName}")]
|
||||
// [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
// [ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
// public async Task<ActionResult> GameScreenshotImage(long GameId, long ScreenshotId, Communications.IGDBAPI_ImageSize Size, string ImageName)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||
|
||||
IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
// IGDB.Models.Screenshot screenshotObject = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), true);
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots");
|
||||
// string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Screenshots");
|
||||
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, screenshotObject.ImageId, Size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.original });
|
||||
// Communications comms = new Communications();
|
||||
// Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, screenshotObject.ImageId, Size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
// string coverFilePath = ImgFetch.Result;
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath))
|
||||
{
|
||||
string filename = screenshotObject.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/jpg";
|
||||
// if (System.IO.File.Exists(coverFilePath))
|
||||
// {
|
||||
// string filename = screenshotObject.ImageId + ".jpg";
|
||||
// string filepath = coverFilePath;
|
||||
// byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
// string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = filename,
|
||||
Inline = true,
|
||||
};
|
||||
// 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");
|
||||
// Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||
// Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||
|
||||
return File(filedata, contentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
// return File(filedata, contentType);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return NotFound();
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// return NotFound();
|
||||
// }
|
||||
// }
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
|
@@ -86,5 +86,24 @@ namespace gaseous_server.Controllers
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -149,35 +149,6 @@ namespace gaseous_server.Controllers
|
||||
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.1")]
|
||||
[HttpPatch]
|
||||
|
@@ -37,7 +37,7 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM Games_Roms) ORDER BY `Name` ASC;";
|
||||
string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM view_Games_Roms) ORDER BY `Name` ASC;";
|
||||
|
||||
List<Platform> RetVal = new List<Platform>();
|
||||
|
||||
@@ -114,22 +114,64 @@ namespace gaseous_server.Controllers
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{PlatformId}/platformlogo/image")]
|
||||
[Route("{PlatformId}/platformlogo/{size}/logo.png")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult PlatformLogoImage(long PlatformId)
|
||||
public async Task<ActionResult> GameImage(long PlatformId, Communications.IGDBAPI_ImageSize size)
|
||||
{
|
||||
try
|
||||
{
|
||||
IGDB.Models.Platform platformObject = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||
|
||||
string logoFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject), "Logo_Medium.png");
|
||||
if (System.IO.File.Exists(logoFilePath))
|
||||
IGDB.Models.PlatformLogo? logoObject = null;
|
||||
try
|
||||
{
|
||||
string filename = "Logo.png";
|
||||
string filepath = logoFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
string contentType = "image/png";
|
||||
logoObject = PlatformLogos.GetPlatformLogo(platformObject.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// getting the logo failed, so we'll try a platform variant if available
|
||||
if (platformObject.Versions != null)
|
||||
{
|
||||
if (platformObject.Versions.Ids.Length > 0)
|
||||
{
|
||||
IGDB.Models.PlatformVersion platformVersion = Classes.Metadata.PlatformVersions.GetPlatformVersion(platformObject.Versions.Ids[0], platformObject);
|
||||
logoObject = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject));
|
||||
string imagePath = Path.Combine(basePath, size.ToString(), logoObject.ImageId + ".jpg");
|
||||
|
||||
if (!System.IO.File.Exists(imagePath))
|
||||
{
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platformObject)), logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
imagePath = ImgFetch.Result;
|
||||
}
|
||||
|
||||
if (!System.IO.File.Exists(imagePath))
|
||||
{
|
||||
Communications comms = new Communications();
|
||||
Task<string> ImgFetch = comms.GetSpecificImageFromServer(basePath, logoObject.ImageId, size, new List<Communications.IGDBAPI_ImageSize> { Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
|
||||
imagePath = ImgFetch.Result;
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(imagePath))
|
||||
{
|
||||
string filename = logoObject.ImageId + ".jpg";
|
||||
string filepath = imagePath;
|
||||
string contentType = "image/jpg";
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
@@ -138,13 +180,21 @@ namespace gaseous_server.Controllers
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@@ -31,63 +31,70 @@ namespace gaseous_server.Controllers
|
||||
[Authorize(Roles = "Admin,Gamer")]
|
||||
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
|
||||
[RequestSizeLimit(long.MaxValue)]
|
||||
[Consumes("multipart/form-data")]
|
||||
[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();
|
||||
|
||||
string workPath = Path.Combine(Config.LibraryConfiguration.LibraryUploadDirectory, sessionid.ToString());
|
||||
|
||||
long size = files.Sum(f => f.Length);
|
||||
|
||||
List<Dictionary<string, object>> UploadedFiles = new List<Dictionary<string, object>>();
|
||||
|
||||
foreach (IFormFile formFile in files)
|
||||
if (file.Length > 0)
|
||||
{
|
||||
if (formFile.Length > 0)
|
||||
Guid FileId = Guid.NewGuid();
|
||||
|
||||
string filePath = Path.Combine(workPath, Path.GetFileName(file.FileName));
|
||||
|
||||
if (!Directory.Exists(workPath))
|
||||
{
|
||||
Guid FileId = Guid.NewGuid();
|
||||
|
||||
string filePath = Path.Combine(workPath, Path.GetFileName(formFile.FileName));
|
||||
|
||||
if (!Directory.Exists(workPath))
|
||||
{
|
||||
Directory.CreateDirectory(workPath);
|
||||
}
|
||||
|
||||
using (var stream = System.IO.File.Create(filePath))
|
||||
{
|
||||
await formFile.CopyToAsync(stream);
|
||||
|
||||
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
||||
UploadedFile.Add("id", FileId.ToString());
|
||||
UploadedFile.Add("originalname", Path.GetFileName(formFile.FileName));
|
||||
UploadedFile.Add("fullpath", filePath);
|
||||
UploadedFiles.Add(UploadedFile);
|
||||
}
|
||||
Directory.CreateDirectory(workPath);
|
||||
}
|
||||
}
|
||||
|
||||
// get override platform if specified
|
||||
IGDB.Models.Platform? OverridePlatform = null;
|
||||
if (OverridePlatformId != null)
|
||||
{
|
||||
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
|
||||
}
|
||||
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
||||
|
||||
// Process uploaded files
|
||||
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
|
||||
{
|
||||
using (var stream = System.IO.File.Create(filePath))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
|
||||
UploadedFile.Add("id", FileId.ToString());
|
||||
UploadedFile.Add("originalname", Path.GetFileName(file.FileName));
|
||||
UploadedFile.Add("fullpath", filePath);
|
||||
}
|
||||
|
||||
// get override platform if specified
|
||||
IGDB.Models.Platform? OverridePlatform = null;
|
||||
if (OverridePlatformId != null)
|
||||
{
|
||||
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
|
||||
}
|
||||
|
||||
// Process uploaded file
|
||||
Classes.ImportGame uploadImport = new ImportGame();
|
||||
uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
Dictionary<string, object> RetVal = uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
|
||||
switch (RetVal["type"])
|
||||
{
|
||||
case "rom":
|
||||
if (RetVal["status"] == "imported")
|
||||
{
|
||||
IGDB.Models.Game? game = (IGDB.Models.Game)RetVal["game"];
|
||||
if (game.Id == null)
|
||||
{
|
||||
RetVal["game"] = Games.GetGame(0, false, false, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (Directory.Exists(workPath))
|
||||
{
|
||||
Directory.Delete(workPath, true);
|
||||
}
|
||||
|
||||
return Ok(RetVal);
|
||||
}
|
||||
|
||||
if (Directory.Exists(workPath))
|
||||
{
|
||||
Directory.Delete(workPath, true);
|
||||
}
|
||||
|
||||
return Ok(new { count = files.Count, size });
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
@@ -73,7 +73,7 @@ namespace gaseous_server.Controllers
|
||||
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
|
||||
{
|
||||
string searchBody = "";
|
||||
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
|
||||
string searchFields = "fields *; ";
|
||||
searchBody += "search \"" + SearchString + "\";";
|
||||
searchBody += "where platforms = (" + PlatformId + ");";
|
||||
searchBody += "limit 100;";
|
||||
@@ -86,12 +86,12 @@ namespace gaseous_server.Controllers
|
||||
// get Game metadata from data source
|
||||
Communications comms = new Communications();
|
||||
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
|
||||
|
||||
|
||||
List<GaseousGame> games = new List<GaseousGame>();
|
||||
foreach (Game game in results.ToList())
|
||||
{
|
||||
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
|
||||
switch(cacheStatus)
|
||||
switch (cacheStatus)
|
||||
{
|
||||
case Storage.CacheStatus.NotPresent:
|
||||
Storage.NewCacheValue(game, false);
|
||||
|
@@ -9,6 +9,7 @@ using gaseous_signature_parser.models.RomSignatureObject;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Asp.Versioning;
|
||||
using gaseous_server.Models;
|
||||
|
||||
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@@ -54,11 +55,34 @@ namespace gaseous_server.Controllers
|
||||
{
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
return signatureManagement.GetByTosecName(TosecName);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public List<Signatures_Sources> GetSignatureSources()
|
||||
{
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
return signatureManagement.GetSources();
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpDelete]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public IActionResult DeleteSignatureSource(int Id)
|
||||
{
|
||||
SignatureManagement signatureManagement = new SignatureManagement();
|
||||
signatureManagement.DeleteSource(Id);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,10 @@ namespace gaseous_server.Controllers
|
||||
List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>();
|
||||
foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
|
||||
{
|
||||
Disks.Add(GetDisk(libraryItem.Path));
|
||||
SystemInfo.PathItem pathItem = GetDisk(libraryItem.Path);
|
||||
pathItem.Name = libraryItem.Name;
|
||||
|
||||
Disks.Add(pathItem);
|
||||
}
|
||||
ReturnValue.Paths = Disks;
|
||||
|
||||
@@ -48,7 +51,7 @@ namespace gaseous_server.Controllers
|
||||
ReturnValue.DatabaseSize = (long)(System.Decimal)dbResponse.Rows[0][1];
|
||||
|
||||
// platform statistics
|
||||
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
|
||||
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN view_Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN view_Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
|
||||
dbResponse = db.ExecuteCMD(sql);
|
||||
ReturnValue.PlatformStatistics = new List<SystemInfo.PlatformStatisticsItem>();
|
||||
foreach (DataRow dr in dbResponse.Rows)
|
||||
@@ -100,7 +103,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
|
||||
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
|
||||
"var FirstRunStatus = " + Config.ReadSetting<string>("FirstRunStatus", "0") + ";" + Environment.NewLine +
|
||||
"var FirstRunStatus = \"" + Config.ReadSetting<string>("FirstRunStatus", "0") + "\";" + Environment.NewLine +
|
||||
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
@@ -324,6 +327,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
public class PathItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string LibraryPath { get; set; }
|
||||
public long SpaceUsed { get; set; }
|
||||
public long SpaceAvailable { get; set; }
|
||||
|
80
gaseous-server/Controllers/V1.1/FileSystemController.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using gaseous_server.Classes;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
using IGDB.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
public class FileSystemController : ControllerBase
|
||||
{
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Authorize(Roles = "Admin")]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult GetFileSystem(string path, bool showFiles = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (path.Contains(".."))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Dictionary<string, List<Dictionary<string, string>>> allFiles = new Dictionary<string, List<Dictionary<string, string>>>();
|
||||
List<Dictionary<string, string>> directories = new List<Dictionary<string, string>>();
|
||||
string[] dirs = Directory.GetDirectories(path);
|
||||
Array.Sort(dirs);
|
||||
foreach (string dir in dirs)
|
||||
{
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(dir);
|
||||
directories.Add(new Dictionary<string, string> { { "name", directoryInfo.Name }, { "path", directoryInfo.FullName } });
|
||||
}
|
||||
allFiles.Add("directories", directories);
|
||||
|
||||
if (showFiles == true)
|
||||
{
|
||||
List<Dictionary<string, string>> files = new List<Dictionary<string, string>>();
|
||||
string[] filePaths = Directory.GetFiles(path);
|
||||
Array.Sort(filePaths);
|
||||
foreach (string file in filePaths)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(file);
|
||||
files.Add(new Dictionary<string, string> { { "name", fileInfo.Name }, { "path", fileInfo.FullName } });
|
||||
}
|
||||
allFiles.Add("files", files);
|
||||
}
|
||||
|
||||
return Ok(allFiles);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,11 +26,11 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class GamesController: ControllerBase
|
||||
public class GamesController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
|
||||
public GamesController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager)
|
||||
@@ -106,7 +106,8 @@ namespace gaseous_server.Controllers.v1_1
|
||||
if (user != null)
|
||||
{
|
||||
string IncludeUnrated = "";
|
||||
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true) {
|
||||
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true)
|
||||
{
|
||||
IncludeUnrated = " OR view_Games.AgeGroupId IS NULL";
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
|
||||
public bool HasSavedGame { get; set; }
|
||||
public bool IsFavourite { get; set; }
|
||||
|
||||
|
||||
|
||||
public class GameRatingItem
|
||||
{
|
||||
@@ -163,7 +164,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
|
||||
public class GameAgeRatingItem
|
||||
{
|
||||
public List<AgeGroups.AgeRestrictionGroupings> AgeGroupings { get; set; } = new List<AgeGroups.AgeRestrictionGroupings>{
|
||||
public List<AgeGroups.AgeRestrictionGroupings> AgeGroupings { get; set; } = new List<AgeGroups.AgeRestrictionGroupings>{
|
||||
AgeGroups.AgeRestrictionGroupings.Child,
|
||||
AgeGroups.AgeRestrictionGroupings.Teen,
|
||||
AgeGroups.AgeRestrictionGroupings.Mature,
|
||||
@@ -186,7 +187,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0)
|
||||
{
|
||||
string whereClause = "";
|
||||
@@ -302,7 +303,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
string platformWhereClause = "";
|
||||
if (model.Platform.Count > 0)
|
||||
{
|
||||
tempVal = " AND Games_Roms.PlatformId IN (";
|
||||
tempVal = " AND view_Games_Roms.PlatformId IN (";
|
||||
for (int i = 0; i < model.Platform.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
@@ -446,7 +447,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
string orderByOrder = "ASC";
|
||||
if (model.Sorting != null)
|
||||
{
|
||||
switch(model.Sorting.SortBy)
|
||||
switch (model.Sorting.SortBy)
|
||||
{
|
||||
case GameSearchModel.GameSortingItem.SortField.NameThe:
|
||||
orderByField = "NameThe";
|
||||
@@ -477,7 +478,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
|
||||
|
||||
string sql = @"
|
||||
SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
|
||||
SELECT DISTINCT
|
||||
@@ -485,6 +486,7 @@ SELECT DISTINCT
|
||||
Game.`Name`,
|
||||
Game.NameThe,
|
||||
Game.Slug,
|
||||
Game.Summary,
|
||||
Game.PlatformId,
|
||||
Game.TotalRating,
|
||||
Game.TotalRatingCount,
|
||||
@@ -509,26 +511,26 @@ FROM
|
||||
WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The')
|
||||
ELSE Game.`Name`
|
||||
END AS NameThe,
|
||||
Games_Roms.PlatformId,
|
||||
view_Games_Roms.PlatformId,
|
||||
AgeGroup.AgeGroupId,
|
||||
COUNT(Games_Roms.Id) AS RomCount
|
||||
COUNT(view_Games_Roms.Id) AS RomCount
|
||||
FROM
|
||||
Game
|
||||
LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId
|
||||
LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + @"
|
||||
LEFT JOIN view_Games_Roms ON Game.Id = view_Games_Roms.GameId" + platformWhereClause + @"
|
||||
LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + @"
|
||||
GROUP BY Game.Id
|
||||
HAVING RomCount > 0) Game
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
|
||||
view_Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
|
||||
FROM
|
||||
GameState
|
||||
JOIN Games_Roms ON GameState.RomId = Games_Roms.Id
|
||||
JOIN view_Games_Roms ON GameState.RomId = view_Games_Roms.Id
|
||||
WHERE
|
||||
GameState.IsMediaGroup = 0
|
||||
AND GameState.UserId = @userid
|
||||
GROUP BY Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
|
||||
GROUP BY view_Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
RomMediaGroup.GameId,
|
||||
@@ -551,7 +553,7 @@ FROM
|
||||
Favourites ON Game.Id = Favourites.GameId AND Favourites.UserId = @userid " + whereClause + " " + havingClause + " " + orderByClause;
|
||||
List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
|
||||
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, whereParams, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 60));
|
||||
|
||||
// get count
|
||||
int RecordCount = dbResponse.Rows.Count;
|
||||
@@ -568,7 +570,7 @@ FROM
|
||||
}
|
||||
}
|
||||
|
||||
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
|
||||
Game retGame = Storage.BuildCacheObject<Game>(new Game(), dbResponse.Rows[i]);
|
||||
Games.MinimalGameItem retMinGame = new Games.MinimalGameItem(retGame);
|
||||
retMinGame.Index = i;
|
||||
if (dbResponse.Rows[i]["RomSaveCount"] != DBNull.Value || dbResponse.Rows[i]["MediaGroupSaveCount"] != DBNull.Value)
|
||||
@@ -593,29 +595,44 @@ FROM
|
||||
|
||||
// build alpha list
|
||||
Dictionary<string, int> AlphaList = new Dictionary<string, int>();
|
||||
int CurrentPage = 1;
|
||||
int NextPageIndex = pageSize;
|
||||
for (int i = 0; i < dbResponse.Rows.Count; i++)
|
||||
if (orderByField == "NameThe" || orderByField == "Name")
|
||||
{
|
||||
string firstChar = dbResponse.Rows[i]["NameThe"].ToString().Substring(0, 1).ToUpperInvariant();
|
||||
if (!"ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(firstChar))
|
||||
int CurrentPage = 1;
|
||||
int NextPageIndex = pageSize;
|
||||
|
||||
string alphaSearchField;
|
||||
if (orderByField == "NameThe")
|
||||
{
|
||||
if (!AlphaList.ContainsKey("#"))
|
||||
{
|
||||
AlphaList.Add("#", 1);
|
||||
}
|
||||
alphaSearchField = "NameThe";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AlphaList.ContainsKey(firstChar))
|
||||
{
|
||||
AlphaList.Add(firstChar, CurrentPage);
|
||||
}
|
||||
alphaSearchField = "Name";
|
||||
}
|
||||
|
||||
for (int i = 0; i < dbResponse.Rows.Count; i++)
|
||||
{
|
||||
if (NextPageIndex == i + 1)
|
||||
{
|
||||
NextPageIndex += pageSize;
|
||||
CurrentPage += 1;
|
||||
}
|
||||
|
||||
string firstChar = dbResponse.Rows[i][alphaSearchField].ToString().Substring(0, 1).ToUpperInvariant();
|
||||
if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(firstChar))
|
||||
{
|
||||
if (!AlphaList.ContainsKey(firstChar))
|
||||
{
|
||||
AlphaList.Add(firstChar, CurrentPage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AlphaList.ContainsKey("#"))
|
||||
{
|
||||
AlphaList.Add("#", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,8 @@ using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Data;
|
||||
using Asp.Versioning;
|
||||
using System.IO.Compression;
|
||||
using gaseous_server.Classes.Metadata;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
@@ -13,7 +15,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[ApiVersion("1.0")]
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
public class StateManagerController: ControllerBase
|
||||
public class StateManagerController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
@@ -39,7 +41,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
byte[] CompressedState = Common.Compress(uploadState.StateByteArray);
|
||||
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
@@ -86,7 +88,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
|
||||
List<Models.GameStateItem> gameStates = new List<GameStateItem>();
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
@@ -116,7 +118,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
@@ -150,7 +152,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
@@ -175,7 +177,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "name", model.Name }
|
||||
};
|
||||
db.ExecuteNonQuery(sql, dbDict);
|
||||
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
@@ -200,7 +202,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
@@ -233,11 +235,11 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("{RomId}/{StateId}/State/")]
|
||||
[Route("{RomId}/{StateId}/State/savestate.state")]
|
||||
public async Task<ActionResult> GetStateDataAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||
public async Task<ActionResult> GetStateDataAsync(long RomId, long StateId, bool IsMediaGroup = false, bool StateOnly = false)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Zipped, State FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
string sql = "SELECT * FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "id", StateId },
|
||||
@@ -246,7 +248,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{ "ismediagroup", IsMediaGroup }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
|
||||
if (data.Rows.Count == 0)
|
||||
{
|
||||
// invalid match - return not found
|
||||
@@ -254,7 +256,27 @@ namespace gaseous_server.Controllers.v1_1
|
||||
}
|
||||
else
|
||||
{
|
||||
string filename = "savestate.state";
|
||||
// get rom data
|
||||
string romName = "";
|
||||
string romMd5 = "";
|
||||
string romSha1 = "";
|
||||
if (IsMediaGroup == false)
|
||||
{
|
||||
Roms.GameRomItem romItem = Roms.GetRom(RomId);
|
||||
romName = romItem.Name;
|
||||
romMd5 = romItem.Md5;
|
||||
romSha1 = romItem.Sha1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RomMediaGroup.GameRomMediaGroupItem mediaGroupItem = RomMediaGroup.GetMediaGroup(RomId);
|
||||
IGDB.Models.Game game = Games.GetGame(mediaGroupItem.GameId, false, false, false);
|
||||
Classes.Common.hashObject hashObject = new Classes.Common.hashObject(Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, mediaGroupItem.Id.ToString() + ".zip"));
|
||||
romName = game.Name;
|
||||
romMd5 = hashObject.md5hash;
|
||||
romSha1 = hashObject.sha1hash;
|
||||
}
|
||||
|
||||
byte[] bytes;
|
||||
if ((bool)data.Rows[0]["Zipped"] == false)
|
||||
{
|
||||
@@ -264,7 +286,85 @@ namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
bytes = Common.Decompress((byte[])data.Rows[0]["State"]);
|
||||
}
|
||||
string contentType = "application/octet-stream";
|
||||
string contentType = "";
|
||||
string filename = ((DateTime)data.Rows[0]["StateDateTime"]).ToString("yyyy-MM-ddTHH-mm-ss") + "-" + Path.GetFileNameWithoutExtension(romName);
|
||||
|
||||
|
||||
if (StateOnly == true)
|
||||
{
|
||||
contentType = "application/octet-stream";
|
||||
filename = filename + ".state";
|
||||
}
|
||||
else
|
||||
{
|
||||
contentType = "application/zip";
|
||||
filename = filename + ".zip";
|
||||
|
||||
Dictionary<string, object> RomInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", romName },
|
||||
{ "StateDateTime", data.Rows[0]["StateDateTime"] },
|
||||
{ "StateName", data.Rows[0]["Name"] }
|
||||
};
|
||||
if ((int)data.Rows[0]["IsMediaGroup"] == 0)
|
||||
{
|
||||
RomInfo.Add("MD5", romMd5);
|
||||
RomInfo.Add("SHA1", romSha1);
|
||||
RomInfo.Add("Type", "ROM");
|
||||
}
|
||||
else
|
||||
{
|
||||
RomInfo.Add("Type", "Media Group");
|
||||
RomInfo.Add("MediaGroupId", (long)data.Rows[0]["RomId"]);
|
||||
}
|
||||
string RomInfoString = Newtonsoft.Json.JsonConvert.SerializeObject(RomInfo, Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore });
|
||||
|
||||
// compile zip file
|
||||
using (var compressedFileStream = new MemoryStream())
|
||||
{
|
||||
List<Dictionary<string, object>> Attachments = new List<Dictionary<string, object>>();
|
||||
Attachments.Add(new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", "savestate.state" },
|
||||
{ "Body", bytes }
|
||||
});
|
||||
// check if value is dbnull
|
||||
if (data.Rows[0]["Screenshot"] != DBNull.Value)
|
||||
{
|
||||
Attachments.Add(new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", "screenshot.jpg" },
|
||||
{ "Body", (byte[])data.Rows[0]["Screenshot"] }
|
||||
});
|
||||
}
|
||||
Attachments.Add(new Dictionary<string, object>
|
||||
{
|
||||
{ "Name", "rominfo.json" },
|
||||
{ "Body", System.Text.Encoding.UTF8.GetBytes(RomInfoString) }
|
||||
});
|
||||
|
||||
//Create an archive and store the stream in memory.
|
||||
using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, false))
|
||||
{
|
||||
foreach (var Attachment in Attachments)
|
||||
{
|
||||
//Create a zip entry for each attachment
|
||||
var zipEntry = zipArchive.CreateEntry(Attachment["Name"].ToString());
|
||||
|
||||
//Get the stream of the attachment
|
||||
using (var originalFileStream = new MemoryStream((byte[])Attachment["Body"]))
|
||||
using (var zipEntryStream = zipEntry.Open())
|
||||
{
|
||||
//Copy the attachment stream to the zip entry stream
|
||||
originalFileStream.CopyTo(zipEntryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return new FileContentResult(compressedFileStream.ToArray(), "application/zip") { FileDownloadName = filename };
|
||||
bytes = compressedFileStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
@@ -279,6 +379,156 @@ namespace gaseous_server.Controllers.v1_1
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[RequestSizeLimit(long.MaxValue)]
|
||||
[Consumes("multipart/form-data")]
|
||||
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
|
||||
[Route("Upload")]
|
||||
public async Task<ActionResult> UploadStateDataAsync(IFormFile file, long RomId = 0, bool IsMediaGroup = false)
|
||||
{
|
||||
// get user
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (file.Length > 0)
|
||||
{
|
||||
MemoryStream fileContent = new MemoryStream();
|
||||
file.CopyTo(fileContent);
|
||||
|
||||
// test if file is a zip file
|
||||
try
|
||||
{
|
||||
using (var zipArchive = new ZipArchive(fileContent, ZipArchiveMode.Read, false))
|
||||
{
|
||||
foreach (var entry in zipArchive.Entries)
|
||||
{
|
||||
if (entry.FullName == "rominfo.json")
|
||||
{
|
||||
using (var stream = entry.Open())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
string RomInfoString = reader.ReadToEnd();
|
||||
Dictionary<string, object> RomInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(RomInfoString);
|
||||
|
||||
// get rom data
|
||||
Roms.GameRomItem romItem;
|
||||
|
||||
try
|
||||
{
|
||||
romItem = Roms.GetRom((string)RomInfo["MD5"]);
|
||||
}
|
||||
catch (Roms.InvalidRomHash)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// get state data
|
||||
byte[] StateData = null;
|
||||
byte[] ScreenshotData = null;
|
||||
string StateName = RomInfo["StateName"].ToString();
|
||||
DateTime StateDateTime = DateTime.Parse(RomInfo["StateDateTime"].ToString());
|
||||
IsMediaGroup = RomInfo["Type"].ToString() == "Media Group" ? true : false;
|
||||
|
||||
if (zipArchive.GetEntry("savestate.state") != null)
|
||||
{
|
||||
using (var stateStream = zipArchive.GetEntry("savestate.state").Open())
|
||||
using (var stateReader = new MemoryStream())
|
||||
{
|
||||
stateStream.CopyTo(stateReader);
|
||||
StateData = stateReader.ToArray();
|
||||
}
|
||||
}
|
||||
if (zipArchive.GetEntry("screenshot.jpg") != null)
|
||||
{
|
||||
using (var screenshotStream = zipArchive.GetEntry("screenshot.jpg").Open())
|
||||
using (var screenshotReader = new MemoryStream())
|
||||
{
|
||||
screenshotStream.CopyTo(screenshotReader);
|
||||
ScreenshotData = screenshotReader.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// save state
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "userid", user.Id },
|
||||
{ "romid", romItem.Id },
|
||||
{ "ismediagroup", IsMediaGroup },
|
||||
{ "statedatetime", StateDateTime },
|
||||
{ "name", StateName },
|
||||
{ "screenshot", ScreenshotData },
|
||||
{ "state", Common.Compress(StateData) },
|
||||
{ "zipped", true }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
RomInfo.Add("RomId", romItem.Id);
|
||||
RomInfo.Add("Management", "Managed");
|
||||
return Ok(RomInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BadRequest("File is not a valid Gaseous state file.");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// not a zip file
|
||||
if (RomId != 0)
|
||||
{
|
||||
// get rom data
|
||||
Roms.GameRomItem romItem;
|
||||
|
||||
try
|
||||
{
|
||||
romItem = Roms.GetRom(RomId);
|
||||
}
|
||||
catch (Roms.InvalidRomHash)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// save state
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "userid", user.Id },
|
||||
{ "romid", RomId },
|
||||
{ "ismediagroup", IsMediaGroup },
|
||||
{ "statedatetime", DateTime.UtcNow },
|
||||
{ "name", "" },
|
||||
{ "screenshot", null },
|
||||
{ "state", Common.Compress(fileContent.ToArray()) },
|
||||
{ "zipped", true }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
return Ok(new Dictionary<string, object>
|
||||
{
|
||||
{ "RomId", RomId },
|
||||
{ "Management", "Unmanaged" }
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest("No rom id provided.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest("File is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
private Models.GameStateItem BuildGameStateItem(DataRow dr)
|
||||
{
|
||||
bool HasScreenshot = true;
|
||||
|
@@ -12,7 +12,7 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[ApiVersion("1.0")]
|
||||
[ApiVersion("1.1")]
|
||||
[ApiController]
|
||||
public class StatisticsController: ControllerBase
|
||||
public class StatisticsController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
@@ -32,15 +32,15 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("Games/{GameId}/")]
|
||||
public async Task<ActionResult> NewRecordStatistics(long GameId)
|
||||
[Route("Games/{GameId}/{PlatformId}/{RomId}")]
|
||||
public async Task<ActionResult> NewRecordStatistics(long GameId, long PlatformId, long RomId, bool IsMediaGroup)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Statistics statistics = new Statistics();
|
||||
return Ok(statistics.RecordSession(Guid.Empty, GameId, user.Id));
|
||||
return Ok(statistics.RecordSession(Guid.Empty, GameId, PlatformId, RomId, IsMediaGroup, user.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -54,15 +54,15 @@ namespace gaseous_server.Controllers.v1_1
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(Models.StatisticsModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[Route("Games/{GameId}/{SessionId}")]
|
||||
public async Task<ActionResult> SubsequentRecordStatistics(long GameId, Guid SessionId)
|
||||
[Route("Games/{GameId}/{PlatformId}/{RomId}/{SessionId}")]
|
||||
public async Task<ActionResult> SubsequentRecordStatistics(long GameId, long PlatformId, long RomId, Guid SessionId, bool IsMediaGroup)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Statistics statistics = new Statistics();
|
||||
return Ok(statistics.RecordSession(SessionId, GameId, user.Id));
|
||||
return Ok(statistics.RecordSession(SessionId, GameId, PlatformId, RomId, IsMediaGroup, user.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
137
gaseous-server/Controllers/V1.1/UserProfileController.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Asp.Versioning;
|
||||
using Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiVersion("1.1")]
|
||||
[Authorize]
|
||||
public class UserProfileController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public UserProfileController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{UserId}")]
|
||||
[ProducesResponseType(typeof(Models.UserProfile), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ResponseCache(CacheProfileName = "Default30")]
|
||||
public ActionResult GetUserProfile(string UserId)
|
||||
{
|
||||
Classes.UserProfile profile = new Classes.UserProfile();
|
||||
Models.UserProfile RetVal = profile.GetUserProfile(UserId);
|
||||
return Ok(RetVal);
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPut]
|
||||
[Route("{UserId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> UpdateUserProfileAsync(string UserId, Models.UserProfile profile)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user.ProfileId.ToString() != UserId)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
Classes.UserProfile userProfile = new Classes.UserProfile();
|
||||
userProfile.UpdateUserProfile(user.Id, profile);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpPut]
|
||||
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
|
||||
[RequestSizeLimit(long.MaxValue)]
|
||||
[Consumes("multipart/form-data")]
|
||||
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
|
||||
[Route("{UserId}/{ProfileImageType}")]
|
||||
public async Task<ActionResult> UpdateAvatarAsync(string UserId, Classes.UserProfile.ImageType ProfileImageType, IFormFile file)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user.ProfileId.ToString() != UserId)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
if (file.Length > 0)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
file.CopyTo(ms);
|
||||
byte[] fileBytes = ms.ToArray();
|
||||
|
||||
Classes.UserProfile userProfile = new Classes.UserProfile();
|
||||
userProfile.UpdateImage(ProfileImageType, UserId, user.Id, file.FileName, fileBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpGet]
|
||||
[Route("{UserId}/{ProfileImageType}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ResponseCache(CacheProfileName = "5Minute")]
|
||||
public async Task<ActionResult> GetAvatarAsync(string UserId, Classes.UserProfile.ImageType ProfileImageType)
|
||||
{
|
||||
Classes.UserProfile userProfile = new Classes.UserProfile();
|
||||
|
||||
Models.ImageItem image = userProfile.GetImage(ProfileImageType, UserId);
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
return File(image.content, image.mimeType, UserId + image.extension);
|
||||
}
|
||||
}
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
[HttpDelete]
|
||||
[Route("{UserId}/{ProfileImageType}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult> DeleteAvatarAsync(string UserId, Classes.UserProfile.ImageType ProfileImageType)
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
if (user.ProfileId.ToString() != UserId)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
Classes.UserProfile userProfile = new Classes.UserProfile();
|
||||
userProfile.DeleteImage(ProfileImageType, user.Id);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,7 +16,7 @@ namespace gaseous_server.Models
|
||||
{
|
||||
var targetType = this.GetType();
|
||||
var sourceType = game.GetType();
|
||||
foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty))
|
||||
foreach (var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty))
|
||||
{
|
||||
// check whether source object has the the property
|
||||
var sp = sourceType.GetProperty(prop.Name);
|
||||
@@ -39,9 +39,11 @@ namespace gaseous_server.Models
|
||||
{
|
||||
if (this.Cover.Id != null)
|
||||
{
|
||||
IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
|
||||
|
||||
return cover;
|
||||
// IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
|
||||
IGDB.Models.Cover cover = new IGDB.Models.Cover()
|
||||
{
|
||||
Id = this.Cover.Id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
9
gaseous-server/Models/ImageItem.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class ImageItem
|
||||
{
|
||||
public byte[] content { get; set; }
|
||||
public string mimeType { get; set; }
|
||||
public string extension { get; set; }
|
||||
}
|
||||
}
|
@@ -13,10 +13,8 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class PlatformMapping
|
||||
{
|
||||
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
|
||||
|
||||
public class PlatformMapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the platform map from the embedded platform map resource
|
||||
/// </summary>
|
||||
@@ -27,7 +25,8 @@ namespace gaseous_server.Models
|
||||
{
|
||||
string rawJson = reader.ReadToEnd();
|
||||
List<PlatformMapItem> platforms = new List<PlatformMapItem>();
|
||||
Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings{
|
||||
Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
MaxDepth = 64
|
||||
};
|
||||
platforms = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson, jsonSerializerSettings);
|
||||
@@ -73,8 +72,8 @@ namespace gaseous_server.Models
|
||||
|
||||
foreach (PlatformMapItem mapItem in platforms)
|
||||
{
|
||||
// get the IGDB platform data
|
||||
Platform platform = Platforms.GetPlatform(mapItem.IGDBId);
|
||||
// insert dummy platform data - it'll be cleaned up on the first metadata refresh
|
||||
Platform platform = CreateDummyPlatform(mapItem);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -92,74 +91,83 @@ namespace gaseous_server.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static IGDB.Models.Platform CreateDummyPlatform(PlatformMapItem mapItem)
|
||||
{
|
||||
IGDB.Models.Platform platform = new IGDB.Models.Platform
|
||||
{
|
||||
Id = mapItem.IGDBId,
|
||||
Name = mapItem.IGDBName,
|
||||
Slug = mapItem.IGDBSlug,
|
||||
AlternativeName = mapItem.AlternateNames.FirstOrDefault()
|
||||
};
|
||||
|
||||
if (Storage.GetCacheStatus("Platform", mapItem.IGDBId) == Storage.CacheStatus.NotPresent)
|
||||
{
|
||||
Storage.NewCacheValue(platform);
|
||||
}
|
||||
|
||||
return platform;
|
||||
}
|
||||
|
||||
public static List<PlatformMapItem> PlatformMap
|
||||
{
|
||||
get
|
||||
{
|
||||
// if (Database.DatabaseMemoryCache.GetCacheObject("PlatformMap") != null)
|
||||
// {
|
||||
// return (List<PlatformMapItem>)Database.DatabaseMemoryCache.GetCacheObject("PlatformMap");
|
||||
// }
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM PlatformMap";
|
||||
DataTable data = db.ExecuteCMD(sql);
|
||||
DataTable data = db.ExecuteCMD(sql); //, new Database.DatabaseMemoryCacheOptions(true, (int)TimeSpan.FromSeconds(5).Ticks));
|
||||
|
||||
List<PlatformMapItem> platformMaps = new List<PlatformMapItem>();
|
||||
foreach (DataRow row in data.Rows)
|
||||
{
|
||||
long mapId = (long)row["Id"];
|
||||
if (PlatformMapCache.ContainsKey(mapId.ToString()))
|
||||
|
||||
PlatformMapItem mapItem = BuildPlatformMapItem(row);
|
||||
if (mapItem != null)
|
||||
{
|
||||
PlatformMapItem mapItem = PlatformMapCache[mapId.ToString()];
|
||||
if (mapItem != null)
|
||||
{
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PlatformMapItem mapItem = BuildPlatformMapItem(row);
|
||||
if (mapItem != null)
|
||||
{
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
}
|
||||
|
||||
platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName));
|
||||
|
||||
//Database.DatabaseMemoryCache.SetCacheObject("PlatformMap", platformMaps, 600);
|
||||
|
||||
return platformMaps;
|
||||
}
|
||||
}
|
||||
|
||||
public static PlatformMapItem GetPlatformMap(long Id)
|
||||
{
|
||||
if (PlatformMapCache.ContainsKey(Id.ToString()))
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Id", Id);
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count > 0)
|
||||
{
|
||||
return PlatformMapCache[Id.ToString()];
|
||||
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]);
|
||||
|
||||
return platformMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
dbDict.Add("Id", Id);
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count > 0)
|
||||
{
|
||||
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]);
|
||||
|
||||
return platformMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception exception = new Exception("Platform Map Id " + Id + " does not exist.");
|
||||
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception);
|
||||
throw exception;
|
||||
}
|
||||
Exception exception = new Exception("Platform Map Id " + Id + " does not exist.");
|
||||
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite)
|
||||
{
|
||||
CreateDummyPlatform(item);
|
||||
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
@@ -238,23 +246,32 @@ namespace gaseous_server.Models
|
||||
{
|
||||
foreach (PlatformMapItem.EmulatorBiosItem biosItem in item.Bios)
|
||||
{
|
||||
sql = "INSERT INTO PlatformMap_Bios (Id, Filename, Description, Hash) VALUES (@Id, @Filename, @Description, @Hash);";
|
||||
bool isEnabled = false;
|
||||
if (item.EnabledBIOSHashes == null)
|
||||
{
|
||||
item.EnabledBIOSHashes = new List<string>();
|
||||
}
|
||||
if (item.EnabledBIOSHashes.Contains(biosItem.hash))
|
||||
{
|
||||
isEnabled = true;
|
||||
}
|
||||
|
||||
sql = "INSERT INTO PlatformMap_Bios (Id, Filename, Description, Hash, Enabled) VALUES (@Id, @Filename, @Description, @Hash, @Enabled);";
|
||||
dbDict.Clear();
|
||||
dbDict.Add("Id", item.IGDBId);
|
||||
dbDict.Add("Filename", biosItem.filename);
|
||||
dbDict.Add("Description", biosItem.description);
|
||||
dbDict.Add("Hash", biosItem.hash);
|
||||
dbDict.Add("Enabled", isEnabled);
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
}
|
||||
}
|
||||
|
||||
if (PlatformMapCache.ContainsKey(item.IGDBId.ToString()))
|
||||
{
|
||||
PlatformMapCache.Remove(item.IGDBId.ToString());
|
||||
}
|
||||
// clear cache
|
||||
Database.DatabaseMemoryCache.RemoveCacheObject("PlatformMap");
|
||||
}
|
||||
|
||||
public static void WriteAvailableEmulators (PlatformMapItem item)
|
||||
public static void WriteAvailableEmulators(PlatformMapItem item)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "";
|
||||
@@ -286,7 +303,16 @@ namespace gaseous_server.Models
|
||||
string sql = "";
|
||||
|
||||
// get platform data
|
||||
IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId);
|
||||
// IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId, false);
|
||||
IGDB.Models.Platform? platform = null;
|
||||
if (Storage.GetCacheStatus("Platform", IGDBId) == Storage.CacheStatus.NotPresent)
|
||||
{
|
||||
//platform = Platforms.GetPlatform(IGDBId, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
platform = (IGDB.Models.Platform)Storage.GetCacheValue<IGDB.Models.Platform>(new Platform(), "id", IGDBId);
|
||||
}
|
||||
|
||||
if (platform != null)
|
||||
{
|
||||
@@ -352,6 +378,7 @@ namespace gaseous_server.Models
|
||||
DataTable biosTable = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
List<PlatformMapItem.EmulatorBiosItem> bioss = new List<PlatformMapItem.EmulatorBiosItem>();
|
||||
List<string> enabledBios = new List<string>();
|
||||
foreach (DataRow biosRow in biosTable.Rows)
|
||||
{
|
||||
PlatformMapItem.EmulatorBiosItem bios = new PlatformMapItem.EmulatorBiosItem
|
||||
@@ -361,6 +388,11 @@ namespace gaseous_server.Models
|
||||
hash = ((string)Common.ReturnValueIfNull(biosRow["Hash"], "")).ToLower()
|
||||
};
|
||||
bioss.Add(bios);
|
||||
|
||||
if ((bool)Common.ReturnValueIfNull(biosRow["Enabled"], true) == true)
|
||||
{
|
||||
enabledBios.Add(bios.hash);
|
||||
}
|
||||
}
|
||||
|
||||
// build item
|
||||
@@ -369,26 +401,20 @@ namespace gaseous_server.Models
|
||||
mapItem.IGDBName = platform.Name;
|
||||
mapItem.IGDBSlug = platform.Slug;
|
||||
mapItem.AlternateNames = alternateNames;
|
||||
mapItem.Extensions = new PlatformMapItem.FileExtensions{
|
||||
mapItem.Extensions = new PlatformMapItem.FileExtensions
|
||||
{
|
||||
SupportedFileExtensions = knownExtensions,
|
||||
UniqueFileExtensions = uniqueExtensions
|
||||
};
|
||||
mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], "");
|
||||
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{
|
||||
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem
|
||||
{
|
||||
Type = (string)Common.ReturnValueIfNull(row["WebEmulator_Type"], ""),
|
||||
Core = (string)Common.ReturnValueIfNull(row["WebEmulator_Core"], ""),
|
||||
AvailableWebEmulators = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem.WebEmulatorItem.AvailableWebEmulatorItem>>((string)Common.ReturnValueIfNull(row["AvailableWebEmulators"], "[]"))
|
||||
};
|
||||
mapItem.Bios = bioss;
|
||||
|
||||
if (PlatformMapCache.ContainsKey(IGDBId.ToString()))
|
||||
{
|
||||
PlatformMapCache[IGDBId.ToString()] = mapItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlatformMapCache.Add(IGDBId.ToString(), mapItem);
|
||||
}
|
||||
mapItem.EnabledBIOSHashes = enabledBios;
|
||||
|
||||
return mapItem;
|
||||
}
|
||||
@@ -455,13 +481,81 @@ namespace gaseous_server.Models
|
||||
}
|
||||
}
|
||||
|
||||
public PlatformMapItem GetUserPlatformMap(string UserId, long PlatformId, long GameId)
|
||||
{
|
||||
// get the system enabled bios hashes
|
||||
Models.PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId);
|
||||
|
||||
// get the user enabled bios hashes
|
||||
PlatformMapping.UserEmulatorConfiguration userEmulatorConfiguration = GetUserEmulator(UserId, GameId, PlatformId);
|
||||
if (userEmulatorConfiguration != null)
|
||||
{
|
||||
platformMapItem.WebEmulator.Type = userEmulatorConfiguration.EmulatorType;
|
||||
platformMapItem.WebEmulator.Core = userEmulatorConfiguration.Core;
|
||||
platformMapItem.EnabledBIOSHashes = userEmulatorConfiguration.EnableBIOSFiles;
|
||||
}
|
||||
|
||||
return platformMapItem;
|
||||
}
|
||||
|
||||
public UserEmulatorConfiguration GetUserEmulator(string UserId, long GameId, long PlatformId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Mapping FROM User_PlatformMap WHERE id = @UserId AND GameId = @GameId AND PlatformId = @PlatformId;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "UserId", UserId },
|
||||
{ "GameId", GameId },
|
||||
{ "PlatformId", PlatformId }
|
||||
};
|
||||
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||
|
||||
if (data.Rows.Count > 0)
|
||||
{
|
||||
UserEmulatorConfiguration emulator = Newtonsoft.Json.JsonConvert.DeserializeObject<UserEmulatorConfiguration>((string)data.Rows[0]["Mapping"]);
|
||||
|
||||
return emulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUserEmulator(string UserId, long GameId, long PlatformId, UserEmulatorConfiguration Mapping)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "INSERT INTO User_PlatformMap (id, GameId, PlatformId, Mapping) VALUES (@UserId, @GameId, @PlatformId, @Mapping) ON DUPLICATE KEY UPDATE Mapping = @Mapping;";
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||
{
|
||||
{ "UserId", UserId },
|
||||
{ "GameId", GameId },
|
||||
{ "PlatformId", PlatformId },
|
||||
{ "Mapping", Newtonsoft.Json.JsonConvert.SerializeObject(Mapping) }
|
||||
};
|
||||
db.ExecuteCMD(sql, dbDict);
|
||||
}
|
||||
|
||||
public void DeleteUserEmulator(string UserId, long GameId, long PlatformId)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "DELETE FROM User_PlatformMap WHERE id = @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 PlatformMapItem
|
||||
{
|
||||
public long IGDBId { get; set; }
|
||||
public string IGDBName { get; set; }
|
||||
public string IGDBSlug { get; set; }
|
||||
public List<string> AlternateNames { get; set; } = new List<string>();
|
||||
|
||||
|
||||
public FileExtensions Extensions { get; set; }
|
||||
public class FileExtensions
|
||||
{
|
||||
@@ -502,7 +596,16 @@ namespace gaseous_server.Models
|
||||
public string description { get; set; }
|
||||
public string filename { get; set; }
|
||||
}
|
||||
|
||||
public List<string> EnabledBIOSHashes { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class UserEmulatorConfiguration
|
||||
{
|
||||
public string EmulatorType { get; set; }
|
||||
public string Core { get; set; }
|
||||
public List<string> EnableBIOSFiles { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
20
gaseous-server/Models/Signatures_Sources.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using NuGet.Protocol.Core.Types;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class Signatures_Sources
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string URL { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Homepage { get; set; }
|
||||
public gaseous_signature_parser.parser.SignatureParser SourceType { get; set; }
|
||||
public string MD5 { get; set; }
|
||||
public string SHA1 { get; set; }
|
||||
}
|
||||
}
|
26
gaseous-server/Models/UserProfile.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using IGDB.Models;
|
||||
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class UserProfile
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Quip { get; set; }
|
||||
public NowPlayingItem? NowPlaying { get; set; }
|
||||
public class NowPlayingItem
|
||||
{
|
||||
public Game Game { get; set; }
|
||||
public Platform Platform { get; set; }
|
||||
public long Duration { get; set; }
|
||||
}
|
||||
public ProfileImageItem? Avatar { get; set; }
|
||||
public ProfileImageItem? ProfileBackground { get; set; }
|
||||
public Dictionary<string, object> Data { get; set; }
|
||||
public class ProfileImageItem
|
||||
{
|
||||
public string MimeType { get; set; }
|
||||
public string Extension { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -36,6 +36,9 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn
|
||||
|
||||
// set up db
|
||||
db.InitDB();
|
||||
// create relation tables if they don't exist
|
||||
Storage.CreateRelationsTables<IGDB.Models.Game>();
|
||||
Storage.CreateRelationsTables<IGDB.Models.Platform>();
|
||||
|
||||
// populate db with static data for lookups
|
||||
AgeRatings.PopulateAgeMap();
|
||||
@@ -45,6 +48,9 @@ Config.InitSettings();
|
||||
// write updated settings back to the config file
|
||||
Config.UpdateConfig();
|
||||
|
||||
// update default library path
|
||||
GameLibrary.UpdateDefaultLibraryPath();
|
||||
|
||||
// set api metadata source from config
|
||||
Communications.MetadataSource = Config.MetadataConfiguration.MetadataSource;
|
||||
|
||||
@@ -64,14 +70,14 @@ if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
|
||||
// kick off any delayed upgrade tasks
|
||||
// run 1002 background updates in the background on every start
|
||||
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
|
||||
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1022);
|
||||
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1023);
|
||||
// start the task
|
||||
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,
|
||||
1,
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.All
|
||||
ProcessQueue.QueueItemType.SignatureIngestor
|
||||
},
|
||||
false,
|
||||
true
|
||||
@@ -320,72 +326,6 @@ app.Use(async (context, next) =>
|
||||
await next();
|
||||
});
|
||||
|
||||
// emergency password recovery if environment variable is set
|
||||
// process:
|
||||
// - set the environment variable "recoveraccount" to the email address of the account to be recovered
|
||||
// - when the server starts the password will be reset to a random string and saved in the library
|
||||
// directory with the name RecoverAccount.txt
|
||||
// - user should copy this password and remove the "recoveraccount" environment variable and the
|
||||
// RecoverAccount.txt file
|
||||
// - the server will not start while the RecoverAccount.txt file exists
|
||||
string PasswordRecoveryFile = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "RecoverAccount.txt");
|
||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
|
||||
{
|
||||
if (File.Exists(PasswordRecoveryFile))
|
||||
{
|
||||
// password has already been set - do nothing and just exit
|
||||
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while recoveraccount environment varibale is set and RecoverAccount.txt file exists.", null, true);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate and save the password to disk
|
||||
int length = 10;
|
||||
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
|
||||
var random = new Random();
|
||||
string password = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
|
||||
File.WriteAllText(PasswordRecoveryFile, password);
|
||||
|
||||
// reset the password
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
|
||||
if (await userManager.FindByNameAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None) != null)
|
||||
{
|
||||
ApplicationUser User = await userManager.FindByEmailAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None);
|
||||
|
||||
//set user password
|
||||
PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
|
||||
User.PasswordHash = ph.HashPassword(User, password);
|
||||
|
||||
await userManager.SetPasswordHashAsync(User, User.PasswordHash, CancellationToken.None);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Server Startup", "Password reset complete, remove the recoveraccount environment variable and RecoverAccount.text file to allow server start.", null, true);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(Logging.LogType.Critical, "Server Startup", "Account to recover not found.", null, true);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if RecoverAccount.text file is present
|
||||
if (File.Exists(PasswordRecoveryFile))
|
||||
{
|
||||
// cannot start while password recovery file exists
|
||||
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while RecoverAccount.txt file exists. Remove the file and try again.", null, true);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// setup library directories
|
||||
Config.LibraryConfiguration.InitLibrary();
|
||||
|
||||
@@ -398,8 +338,8 @@ gaseous_server.Classes.Metadata.Platforms.AssignAllPlatformsToGameIdZero();
|
||||
// extract platform map if not present
|
||||
PlatformMapping.ExtractPlatformMap();
|
||||
|
||||
// force load platform map into cache
|
||||
var platformMap = PlatformMapping.PlatformMap;
|
||||
// migrate old firmware directory structure to new style
|
||||
Bios.MigrateToNewFolderStructure();
|
||||
|
||||
// add background tasks
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
|
@@ -1,40 +1 @@
|
||||
CREATE TABLE `Signatures_RomToSource` (
|
||||
`SourceId` int NOT NULL,
|
||||
`RomId` int NOT NULL,
|
||||
PRIMARY KEY (`SourceId`, `RomId`)
|
||||
);
|
||||
|
||||
CREATE TABLE `Signatures_Games_Countries` (
|
||||
`GameId` INT NOT NULL,
|
||||
`CountryId` INT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `CountryId`),
|
||||
CONSTRAINT `GameCountry` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE `Signatures_Games_Languages` (
|
||||
`GameId` INT NOT NULL,
|
||||
`LanguageId` INT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `LanguageId`),
|
||||
CONSTRAINT `GameLanguage` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE `Country` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT,
|
||||
`Code` VARCHAR(20) NULL,
|
||||
`Value` VARCHAR(255) NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
INDEX `id_Code` (`Code` ASC) VISIBLE,
|
||||
INDEX `id_Value` (`Value` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Language` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT,
|
||||
`Code` VARCHAR(20) NULL,
|
||||
`Value` VARCHAR(255) NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
INDEX `id_Code` (`Code` ASC) VISIBLE,
|
||||
INDEX `id_Value` (`Value` ASC) VISIBLE
|
||||
);
|
||||
|
||||
ALTER TABLE `Games_Roms`
|
||||
ADD COLUMN `RomDataVersion` INT DEFAULT 1;
|
||||
ALTER TABLE `Platform` CHANGE `Name` `Name` varchar(255);
|
17
gaseous-server/Support/Database/MySQL/gaseous-1023.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
CREATE TABLE `Country` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT,
|
||||
`Code` VARCHAR(20) NULL,
|
||||
`Value` VARCHAR(255) NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
INDEX `id_Code` (`Code` ASC) VISIBLE,
|
||||
INDEX `id_Value` (`Value` ASC) VISIBLE
|
||||
);
|
||||
|
||||
CREATE TABLE `Language` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT,
|
||||
`Code` VARCHAR(20) NULL,
|
||||
`Value` VARCHAR(255) NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
INDEX `id_Code` (`Code` ASC) VISIBLE,
|
||||
INDEX `id_Value` (`Value` ASC) VISIBLE
|
||||
);
|
99
gaseous-server/Support/Database/MySQL/gaseous-1024.sql
Normal file
@@ -0,0 +1,99 @@
|
||||
CREATE TABLE `Signatures_RomToSource` (
|
||||
`SourceId` int NOT NULL,
|
||||
`RomId` int NOT NULL,
|
||||
PRIMARY KEY (`SourceId`, `RomId`)
|
||||
);
|
||||
|
||||
CREATE TABLE `Signatures_Games_Countries` (
|
||||
`GameId` INT NOT NULL,
|
||||
`CountryId` INT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `CountryId`),
|
||||
CONSTRAINT `GameCountry` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE `Signatures_Games_Languages` (
|
||||
`GameId` INT NOT NULL,
|
||||
`LanguageId` INT NOT NULL,
|
||||
PRIMARY KEY (`GameId`, `LanguageId`),
|
||||
CONSTRAINT `GameLanguage` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
ALTER TABLE `Games_Roms` ADD COLUMN `RomDataVersion` INT DEFAULT 1;
|
||||
|
||||
CREATE TABLE UserProfiles (
|
||||
`Id` VARCHAR(45) NOT NULL,
|
||||
`UserId` VARCHAR(45) NOT NULL,
|
||||
`DisplayName` VARCHAR(255) NOT NULL,
|
||||
`Quip` VARCHAR(255) NOT NULL,
|
||||
`Avatar` LONGBLOB,
|
||||
`AvatarExtension` CHAR(6),
|
||||
`ProfileBackground` LONGBLOB,
|
||||
`ProfileBackgroundExtension` CHAR(6),
|
||||
`UnstructuredData` LONGTEXT NOT NULL,
|
||||
PRIMARY KEY (`Id`, `UserId`)
|
||||
);
|
||||
|
||||
ALTER TABLE `PlatformMap_Bios`
|
||||
ADD COLUMN `Enabled` BOOLEAN DEFAULT TRUE;
|
||||
|
||||
CREATE TABLE `User_PlatformMap` (
|
||||
`id` VARCHAR(128) NOT NULL,
|
||||
`GameId` BIGINT NOT NULL,
|
||||
`PlatformId` BIGINT NOT NULL,
|
||||
`Mapping` LONGTEXT,
|
||||
PRIMARY KEY (`id`, `GameId`, `PlatformId`),
|
||||
CONSTRAINT `User_PlatformMap_UserId` FOREIGN KEY (`id`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE `UserTimeTracking`
|
||||
ADD COLUMN `PlatformId` BIGINT,
|
||||
ADD COLUMN `IsMediaGroup` BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN `RomId` BIGINT;
|
||||
|
||||
CREATE TABLE `User_RecentPlayedRoms` (
|
||||
`UserId` varchar(128) NOT NULL,
|
||||
`GameId` bigint(20) NOT NULL,
|
||||
`PlatformId` bigint(20) NOT NULL,
|
||||
`RomId` bigint(20) NOT NULL,
|
||||
`IsMediaGroup` tinyint(1) DEFAULT NULL,
|
||||
PRIMARY KEY (
|
||||
`UserId`,
|
||||
`GameId`,
|
||||
`PlatformId`
|
||||
),
|
||||
CONSTRAINT `RecentPlayedRoms_Users` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE `User_GameFavouriteRoms` (
|
||||
`UserId` varchar(128) NOT NULL,
|
||||
`GameId` bigint(20) NOT NULL,
|
||||
`PlatformId` bigint(20) NOT NULL,
|
||||
`RomId` bigint(20) NOT NULL,
|
||||
`IsMediaGroup` tinyint(1) DEFAULT NULL,
|
||||
PRIMARY KEY (
|
||||
`UserId`,
|
||||
`GameId`,
|
||||
`PlatformId`
|
||||
),
|
||||
CONSTRAINT `GameFavouriteRoms_Users` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE `Games_Roms`
|
||||
CHANGE `Path` `RelativePath` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL;
|
||||
|
||||
ALTER TABLE `Games_Roms`
|
||||
ADD CONSTRAINT Games_Roms_LibraryId FOREIGN KEY (`LibraryId`) REFERENCES `GameLibraries` (`Id`) ON DELETE CASCADE;
|
||||
|
||||
CREATE VIEW view_Games_Roms AS
|
||||
SELECT `Games_Roms`.*, CONCAT(
|
||||
`GameLibraries`.`Path`, '/', `Games_Roms`.`RelativePath`
|
||||
) AS `Path`, `GameLibraries`.`Name` AS `LibraryName`
|
||||
FROM
|
||||
`Games_Roms`
|
||||
JOIN `GameLibraries` ON `Games_Roms`.`LibraryId` = `GameLibraries`.`Id`;
|
||||
|
||||
CREATE VIEW view_UserTimeTracking AS
|
||||
SELECT *, DATE_ADD(
|
||||
SessionTime, INTERVAL SessionLength MINUTE
|
||||
) AS SessionEnd
|
||||
FROM UserTimeTracking;
|
@@ -67,6 +67,8 @@
|
||||
<None Remove="Support\Database\MySQL\gaseous-1020.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1021.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1022.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1023.sql" />
|
||||
<None Remove="Support\Database\MySQL\gaseous-1024.sql" />
|
||||
<None Remove="Classes\Metadata\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -114,5 +116,7 @@
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1022.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1023.sql" />
|
||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1024.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
gaseous-server/wwwroot/.DS_Store
vendored
@@ -22,9 +22,11 @@
|
||||
if (StateUrl) {
|
||||
console.log('Loading saved state from: ' + StateUrl);
|
||||
EJS_loadStateURL = StateUrl;
|
||||
EJS_startOnLoaded = true;
|
||||
}
|
||||
|
||||
// start the emulator automatically when loaded
|
||||
EJS_startOnLoaded = true;
|
||||
|
||||
// Path to the data directory
|
||||
EJS_pathtodata = '/emulators/EmulatorJS/data/';
|
||||
|
||||
@@ -42,10 +44,11 @@
|
||||
|
||||
EJS_Buttons = {
|
||||
saveSavFiles: false,
|
||||
loadSavFiles: false
|
||||
loadSavFiles: false,
|
||||
exitEmulation: false
|
||||
}
|
||||
|
||||
EJS_onSaveState = function(e) {
|
||||
EJS_onSaveState = function (e) {
|
||||
var returnValue = {
|
||||
"ScreenshotByteArrayBase64": btoa(Uint8ToString(e.screenshot)),
|
||||
"StateByteArrayBase64": btoa(Uint8ToString(e.state))
|
||||
@@ -72,8 +75,12 @@
|
||||
returnValue = undefined;
|
||||
}
|
||||
|
||||
EJS_onLoadState = function(e) {
|
||||
showDialog('emulatorloadstate', { "romId": romId, "IsMediaGroup": IsMediaGroup });
|
||||
EJS_onLoadState = function (e) {
|
||||
let rompath = decodeURIComponent(getQueryString('rompath', 'string'));
|
||||
rompath = rompath.substring(rompath.lastIndexOf('/') + 1);
|
||||
console.log(rompath);
|
||||
let stateManager = new EmulatorStateManager(romId, IsMediaGroup, getQueryString('engine', 'string'), getQueryString('core', 'string'), platformId, gameId, rompath);
|
||||
stateManager.open();
|
||||
}
|
||||
</script>
|
||||
<script src='/emulators/EmulatorJS/data/loader.js'></script>
|
16
gaseous-server/wwwroot/images/Critical.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>stop-warning</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="invisible_box" data-name="invisible box">
|
||||
<rect width="48" height="48" fill="none"/>
|
||||
</g>
|
||||
<g id="icons_Q2" data-name="icons Q2">
|
||||
<g>
|
||||
<path d="M43.4,15.1,32.9,4.6A2,2,0,0,0,31.5,4h-15a2,2,0,0,0-1.4.6L4.6,15.1A2,2,0,0,0,4,16.5v15a2,2,0,0,0,.6,1.4L15.1,43.4a2,2,0,0,0,1.4.6h15a2,2,0,0,0,1.4-.6L43.4,32.9a2,2,0,0,0,.6-1.4v-15A2,2,0,0,0,43.4,15.1ZM40,30.6,30.6,40H17.4L8,30.6V17.4L17.4,8H30.6L40,17.4Z"/>
|
||||
<path d="M26.8,24l5.6-5.6a2,2,0,0,0-2.8-2.8L24,21.2l-5.6-5.6a2,2,0,0,0-2.8,2.8L21.2,24l-5.6,5.6a1.9,1.9,0,0,0,0,2.8,1.9,1.9,0,0,0,2.8,0L24,26.8l5.6,5.6a1.9,1.9,0,0,0,2.8,0,1.9,1.9,0,0,0,0-2.8Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
After Width: | Height: | Size: 969 B |
8
gaseous-server/wwwroot/images/NoIntro-logo.svg
Normal file
After Width: | Height: | Size: 480 KiB |
1
gaseous-server/wwwroot/images/Ratings/ESRB/EC.svg
Normal file
After Width: | Height: | Size: 8.3 KiB |
17
gaseous-server/wwwroot/images/Warning.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>warning</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="invisible_box" data-name="invisible box">
|
||||
<rect width="48" height="48" fill="none"/>
|
||||
</g>
|
||||
<g id="icons_Q2" data-name="icons Q2">
|
||||
<g>
|
||||
<path d="M24,9,40.6,39H7.5L24,9M2.3,40A2,2,0,0,0,4,43H44a2,2,0,0,0,1.7-3L25.7,4a2,2,0,0,0-3.4,0Z"/>
|
||||
<path d="M22,19v9a2,2,0,0,0,4,0V19a2,2,0,0,0-4,0Z"/>
|
||||
<circle cx="24" cy="34" r="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
After Width: | Height: | Size: 695 B |
12
gaseous-server/wwwroot/images/arrow-down.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512.011 512.011" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M505.755,123.592c-8.341-8.341-21.824-8.341-30.165,0L256.005,343.176L36.421,123.592c-8.341-8.341-21.824-8.341-30.165,0
|
||||
s-8.341,21.824,0,30.165l234.667,234.667c4.16,4.16,9.621,6.251,15.083,6.251c5.462,0,10.923-2.091,15.083-6.251l234.667-234.667
|
||||
C514.096,145.416,514.096,131.933,505.755,123.592z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 684 B |
12
gaseous-server/wwwroot/images/arrow-left.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512.006 512.006" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M388.419,475.59L168.834,256.005L388.418,36.421c8.341-8.341,8.341-21.824,0-30.165s-21.824-8.341-30.165,0
|
||||
L123.586,240.923c-8.341,8.341-8.341,21.824,0,30.165l234.667,234.667c4.16,4.16,9.621,6.251,15.083,6.251
|
||||
c5.461,0,10.923-2.091,15.083-6.251C396.76,497.414,396.76,483.931,388.419,475.59z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 679 B |
12
gaseous-server/wwwroot/images/arrow-right.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512.005 512.005" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M388.418,240.923L153.751,6.256c-8.341-8.341-21.824-8.341-30.165,0s-8.341,21.824,0,30.165L343.17,256.005
|
||||
L123.586,475.589c-8.341,8.341-8.341,21.824,0,30.165c4.16,4.16,9.621,6.251,15.083,6.251c5.461,0,10.923-2.091,15.083-6.251
|
||||
l234.667-234.667C396.759,262.747,396.759,249.264,388.418,240.923z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 682 B |
11
gaseous-server/wwwroot/images/arrow-up.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M505.752,358.248L271.085,123.582c-8.331-8.331-21.839-8.331-30.17,0L6.248,358.248c-8.331,8.331-8.331,21.839,0,30.17
|
||||
s21.839,8.331,30.17,0L256,168.837l219.582,219.582c8.331,8.331,21.839,8.331,30.17,0S514.083,366.58,505.752,358.248z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 609 B |
69
gaseous-server/wwwroot/images/bios.svg
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<rect x="342.232" y="460.8" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="234.442" y="460.8" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="126.653" y="460.8" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="8.084" y="234.442" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
<rect x="8.084" y="342.232" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
<rect x="460.8" y="342.232" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
<rect x="460.8" y="234.442" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
<rect x="460.8" y="126.653" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
<rect x="342.232" y="13.474" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="234.442" y="13.474" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="126.653" y="13.474" style="fill:#AFB6BB;" width="43.116" height="37.726"/>
|
||||
<rect x="8.084" y="126.653" style="fill:#AFB6BB;" width="43.116" height="43.116"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="51.2" y="126.653" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="51.2" y="234.442" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="51.2" y="342.232" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="126.653" y="428.463" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
<rect x="234.442" y="428.463" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
<rect x="342.232" y="428.463" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
<rect x="428.463" y="342.232" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="428.463" y="234.442" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="428.463" y="126.653" style="fill:#E7ECED;" width="32.337" height="43.116"/>
|
||||
<rect x="342.232" y="51.2" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
<rect x="234.442" y="51.2" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
<rect x="126.653" y="51.2" style="fill:#E7ECED;" width="43.116" height="32.337"/>
|
||||
</g>
|
||||
<path style="fill:#595E62;" d="M428.463,385.347v32.337c0,5.928-4.851,10.779-10.779,10.779h-32.337h-43.116h-64.674h-43.116
|
||||
h-64.674h-43.116H94.316c-5.928,0-10.779-4.851-10.779-10.779v-32.337v-43.116v-64.674v-43.116v-64.674v-43.116V94.316
|
||||
c0-5.928,4.851-10.779,10.779-10.779h32.337h43.116h64.674h43.116h64.674h43.116h32.337c5.928,0,10.779,4.851,10.779,10.779v32.337
|
||||
v43.116v64.674v43.116v64.674V385.347z M385.347,385.347V126.653H126.653v258.695H385.347z"/>
|
||||
<rect x="126.653" y="126.653" style="fill:#36C63F;" width="258.695" height="258.695"/>
|
||||
<path style="fill:#00AB4E;" d="M173.785,126.653h-47.132v258.695h258.695v-47.132C284.709,306.659,205.341,227.291,173.785,126.653z
|
||||
"/>
|
||||
<path style="fill:#44494C;" d="M126.653,385.347V126.653H256V83.537h-21.558h-64.674h-43.116H94.316
|
||||
c-5.928,0-10.779,4.851-10.779,10.779v32.337v43.116v64.674v43.116v64.674v43.116v32.337c0,5.928,4.851,10.779,10.779,10.779h32.337
|
||||
h43.116h64.674H256v-43.116H126.653z"/>
|
||||
<g>
|
||||
<rect x="180.547" y="161.684" style="fill:#FFFFFF;" width="150.905" height="16.168"/>
|
||||
<rect x="180.547" y="237.137" style="fill:#FFFFFF;" width="150.905" height="16.168"/>
|
||||
<rect x="180.547" y="199.411" style="fill:#FFFFFF;" width="26.947" height="16.168"/>
|
||||
<rect x="180.547" y="334.147" style="fill:#FFFFFF;" width="26.947" height="16.168"/>
|
||||
<rect x="223.663" y="334.147" style="fill:#FFFFFF;" width="26.947" height="16.168"/>
|
||||
<rect x="299.116" y="334.147" style="fill:#FFFFFF;" width="26.947" height="16.168"/>
|
||||
<rect x="239.832" y="199.411" style="fill:#FFFFFF;" width="91.621" height="16.168"/>
|
||||
</g>
|
||||
<path d="M118.568,393.432h274.863V118.568H118.568V393.432z M134.737,134.737h242.526v242.526H134.737V134.737z"/>
|
||||
<path d="M512,177.853v-59.284h-75.453V94.316c0-10.401-8.463-18.863-18.863-18.863h-24.253V5.389h-59.284v70.063h-48.505V5.389
|
||||
h-59.284v70.063h-48.505V5.389h-59.284v70.063H94.316c-10.401,0-18.863,8.463-18.863,18.863v24.253H0v59.284h75.453v48.505H0v59.284
|
||||
h75.453v48.505H0v59.284h75.453v24.253c0,10.401,8.463,18.863,18.863,18.863h24.253v70.063h59.284v-70.063h48.505v70.063h59.284
|
||||
v-70.063h48.505v70.063h59.284v-70.063h24.253c10.401,0,18.863-8.463,18.863-18.863v-24.253H512v-59.284h-75.453v-48.505H512
|
||||
v-59.284h-75.453v-48.505H512z M436.547,134.737h16.168v26.947h-16.168V134.737z M495.832,161.684h-26.947v-26.947h26.947V161.684z
|
||||
M350.316,59.284h26.947v16.168h-26.947V59.284z M377.263,21.558v21.558h-26.947V21.558H377.263z M242.526,59.284h26.947v16.168
|
||||
h-26.947V59.284z M269.474,21.558v21.558h-26.947V21.558H269.474z M134.737,59.284h26.947v16.168h-26.947V59.284z M161.684,21.558
|
||||
v21.558h-26.947V21.558H161.684z M75.453,161.684H59.284v-26.947h16.168V161.684z M16.168,134.737h26.947v26.947H16.168V134.737z
|
||||
M75.453,269.474H59.284v-26.947h16.168V269.474z M16.168,242.526h26.947v26.947H16.168V242.526z M75.453,377.263H59.284v-26.947
|
||||
h16.168V377.263z M16.168,350.316h26.947v26.947H16.168V350.316z M161.684,452.716h-26.947v-16.168h26.947V452.716z
|
||||
M134.737,490.442v-21.558h26.947v21.558H134.737z M269.474,452.716h-26.947v-16.168h26.947V452.716z M242.526,490.442v-21.558
|
||||
h26.947v21.558H242.526z M377.263,452.716h-26.947v-16.168h26.947V452.716z M350.316,490.442v-21.558h26.947v21.558H350.316z
|
||||
M436.547,350.316h16.168v26.947h-16.168V350.316z M495.832,377.263h-26.947v-26.947h26.947V377.263z M436.547,242.526h16.168
|
||||
v26.947h-16.168V242.526z M495.832,269.474h-26.947v-26.947h26.947V269.474z M420.379,417.684c0,1.461-1.234,2.695-2.695,2.695
|
||||
H94.316c-1.461,0-2.695-1.234-2.695-2.695V94.316c0-1.461,1.234-2.695,2.695-2.695h323.368c1.461,0,2.695,1.234,2.695,2.695V417.684
|
||||
z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
17
gaseous-server/wwwroot/images/cross.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
|
||||
<title>cross-circle</title>
|
||||
<desc>Created with Sketch Beta.</desc>
|
||||
<defs>
|
||||
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="Icon-Set" sketch:type="MSLayerGroup" transform="translate(-568.000000, -1087.000000)" fill="red">
|
||||
<path d="M584,1117 C576.268,1117 570,1110.73 570,1103 C570,1095.27 576.268,1089 584,1089 C591.732,1089 598,1095.27 598,1103 C598,1110.73 591.732,1117 584,1117 L584,1117 Z M584,1087 C575.163,1087 568,1094.16 568,1103 C568,1111.84 575.163,1119 584,1119 C592.837,1119 600,1111.84 600,1103 C600,1094.16 592.837,1087 584,1087 L584,1087 Z M589.717,1097.28 C589.323,1096.89 588.686,1096.89 588.292,1097.28 L583.994,1101.58 L579.758,1097.34 C579.367,1096.95 578.733,1096.95 578.344,1097.34 C577.953,1097.73 577.953,1098.37 578.344,1098.76 L582.58,1102.99 L578.314,1107.26 C577.921,1107.65 577.921,1108.29 578.314,1108.69 C578.708,1109.08 579.346,1109.08 579.74,1108.69 L584.006,1104.42 L588.242,1108.66 C588.633,1109.05 589.267,1109.05 589.657,1108.66 C590.048,1108.27 590.048,1107.63 589.657,1107.24 L585.42,1103.01 L589.717,1098.71 C590.11,1098.31 590.11,1097.68 589.717,1097.28 L589.717,1097.28 Z" id="cross-circle" sketch:type="MSShapeGroup">
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
4
gaseous-server/wwwroot/images/discord.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.59 5.88997C17.36 5.31997 16.05 4.89997 14.67 4.65997C14.5 4.95997 14.3 5.36997 14.17 5.69997C12.71 5.47997 11.26 5.47997 9.83001 5.69997C9.69001 5.36997 9.49001 4.95997 9.32001 4.65997C7.94001 4.89997 6.63001 5.31997 5.40001 5.88997C2.92001 9.62997 2.25001 13.28 2.58001 16.87C4.23001 18.1 5.82001 18.84 7.39001 19.33C7.78001 18.8 8.12001 18.23 8.42001 17.64C7.85001 17.43 7.31001 17.16 6.80001 16.85C6.94001 16.75 7.07001 16.64 7.20001 16.54C10.33 18 13.72 18 16.81 16.54C16.94 16.65 17.07 16.75 17.21 16.85C16.7 17.16 16.15 17.42 15.59 17.64C15.89 18.23 16.23 18.8 16.62 19.33C18.19 18.84 19.79 18.1 21.43 16.87C21.82 12.7 20.76 9.08997 18.61 5.88997H18.59ZM8.84001 14.67C7.90001 14.67 7.13001 13.8 7.13001 12.73C7.13001 11.66 7.88001 10.79 8.84001 10.79C9.80001 10.79 10.56 11.66 10.55 12.73C10.55 13.79 9.80001 14.67 8.84001 14.67ZM15.15 14.67C14.21 14.67 13.44 13.8 13.44 12.73C13.44 11.66 14.19 10.79 15.15 10.79C16.11 10.79 16.87 11.66 16.86 12.73C16.86 13.79 16.11 14.67 15.15 14.67Z" fill="#000000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
gaseous-server/wwwroot/images/github-mark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
After Width: | Height: | Size: 963 B |
5
gaseous-server/wwwroot/images/hasheous.svg
Normal file
After Width: | Height: | Size: 567 KiB |
4
gaseous-server/wwwroot/images/home.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3103 1.77586C11.6966 1.40805 12.3034 1.40805 12.6897 1.77586L20.6897 9.39491L23.1897 11.7759C23.5896 12.1567 23.605 12.7897 23.2241 13.1897C22.8433 13.5896 22.2103 13.605 21.8103 13.2241L21 12.4524V20C21 21.1046 20.1046 22 19 22H14H10H5C3.89543 22 3 21.1046 3 20V12.4524L2.18966 13.2241C1.78972 13.605 1.15675 13.5896 0.775862 13.1897C0.394976 12.7897 0.410414 12.1567 0.810345 11.7759L3.31034 9.39491L11.3103 1.77586ZM5 10.5476V20H9V15C9 13.3431 10.3431 12 12 12C13.6569 12 15 13.3431 15 15V20H19V10.5476L12 3.88095L5 10.5476ZM13 20V15C13 14.4477 12.5523 14 12 14C11.4477 14 11 14.4477 11 15V20H13Z" fill="#000000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 901 B |