UI overhaul (#383)
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -11,11 +11,13 @@ 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}
|
||||
|
@@ -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
|
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"]
|
@@ -16,9 +16,9 @@ Version 1.7.0 and later contain user authentication, and can be exposed to the i
|
||||
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
|
||||
|
41
build/Dockerfile
Normal file
@@ -0,0 +1,41 @@
|
||||
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
|
||||
|
||||
# disabled for 1.7.4 as the next version EmulatorJS is not yet available
|
||||
# # 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
|
||||
RUN wget --recursive --no-parent https://cdn.emulatorjs.org/latest/
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN cp -fr cdn.emulatorjs.org/latest/* out/wwwroot/emulators/EmulatorJS
|
||||
RUN rm -Rf cdn.emulatorjs.org
|
||||
|
||||
# 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 .
|
||||
|
||||
# start gaseous-server
|
||||
ENTRYPOINT ["dotnet", "gaseous-server.dll"]
|
61
build/Dockerfile-EmbeddedDB
Normal file
@@ -0,0 +1,61 @@
|
||||
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
|
||||
|
||||
# disabled for 1.7.4 as the next version EmulatorJS is not yet available
|
||||
# # 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
|
||||
RUN wget --recursive --no-parent https://cdn.emulatorjs.org/latest/
|
||||
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||
RUN cp -fr cdn.emulatorjs.org/latest/* out/wwwroot/emulators/EmulatorJS
|
||||
RUN rm -Rf cdn.emulatorjs.org
|
||||
|
||||
# Build runtime image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
ENV INDOCKER=1
|
||||
WORKDIR /App
|
||||
COPY --from=build-env /App/out .
|
||||
|
||||
# variables
|
||||
ENV dbhost=localhost
|
||||
ENV dbuser=root
|
||||
ENV dbpass=gaseous
|
||||
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/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/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# clean up apt-get
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists
|
||||
|
||||
# volumes
|
||||
VOLUME /root/.gaseous-server /var/lib/mysql
|
||||
|
||||
# start services
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
9
build/mariadb.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Wait for the service to start
|
||||
while ! mysqladmin ping -h localhost --silent; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Set the root password
|
||||
mariadb -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$MARIADB_ROOT_PASSWORD';"
|
31
build/supervisord.conf
Normal file
@@ -0,0 +1,31 @@
|
||||
[supervisord]
|
||||
user=root
|
||||
nodaemon=true
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
pidfile=/var/run/supervisord.pid
|
||||
loglevel = INFO
|
||||
|
||||
[program:mariadb]
|
||||
command=/usr/sbin/mariadbd --user=root
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
|
||||
[program:mariadb-setup]
|
||||
command=bash -c "/usr/sbin/start-mariadb.sh"
|
||||
autostart=true
|
||||
autorestart=false
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
|
||||
[program:gaseous-server]
|
||||
command=dotnet /App/gaseous-server.dll
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
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; }
|
||||
}
|
||||
}
|
||||
|
@@ -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"];
|
||||
|
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
@@ -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,8 +8,9 @@ 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 = "";
|
||||
|
@@ -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);
|
||||
|
@@ -197,11 +197,6 @@ namespace gaseous_server.Classes
|
||||
{
|
||||
string SettingName = (string)dataRow["Setting"];
|
||||
|
||||
if (SettingName.StartsWith("LastRun_"))
|
||||
{
|
||||
Console.WriteLine("Break");
|
||||
}
|
||||
|
||||
if (AppSettings.ContainsKey(SettingName))
|
||||
{
|
||||
AppSettings.Remove(SettingName);
|
||||
|
@@ -73,6 +73,8 @@ namespace gaseous_server.Classes
|
||||
// load resources
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
DatabaseMemoryCacheOptions? CacheOptions = new DatabaseMemoryCacheOptions(false);
|
||||
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
@@ -80,22 +82,22 @@ namespace gaseous_server.Classes
|
||||
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);
|
||||
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)
|
||||
{
|
||||
@@ -118,7 +120,7 @@ namespace gaseous_server.Classes
|
||||
// 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?
|
||||
@@ -140,12 +142,12 @@ namespace gaseous_server.Classes
|
||||
|
||||
// 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);
|
||||
@@ -170,39 +172,83 @@ namespace gaseous_server.Classes
|
||||
|
||||
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, 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)
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, 30, "");
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
return _ExecuteCMD(Command, Parameters, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions)
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
return _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
}
|
||||
|
||||
public DataTable ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command)
|
||||
{
|
||||
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, DatabaseMemoryCacheOptions? CacheOptions)
|
||||
{
|
||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||
return _ExecuteCMDDict(Command, dbDict, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters)
|
||||
{
|
||||
return _ExecuteCMDDict(Command, Parameters, 30, "");
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
return _ExecuteCMDDict(Command, Parameters, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions)
|
||||
{
|
||||
return _ExecuteCMDDict(Command, Parameters, CacheOptions, 30, "");
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
return _ExecuteCMDDict(Command, Parameters, Timeout, ConnectionString);
|
||||
DatabaseMemoryCacheOptions? CacheOptions = null;
|
||||
|
||||
return _ExecuteCMDDict(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
}
|
||||
|
||||
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
DataTable dataTable = _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
|
||||
return _ExecuteCMDDict(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
}
|
||||
|
||||
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
DataTable dataTable = _ExecuteCMD(Command, Parameters, CacheOptions, Timeout, ConnectionString);
|
||||
|
||||
// convert datatable to dictionary
|
||||
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
|
||||
@@ -228,14 +274,45 @@ namespace gaseous_server.Classes
|
||||
return rows;
|
||||
}
|
||||
|
||||
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
|
||||
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, DatabaseMemoryCacheOptions? CacheOptions, int Timeout = 30, string ConnectionString = "")
|
||||
{
|
||||
string CacheKey = Command + string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
|
||||
|
||||
if (CacheOptions is object && CacheOptions.CacheEnabled)
|
||||
{
|
||||
object? CachedData = DatabaseMemoryCache.GetCacheObject(CacheKey);
|
||||
if (CachedData is object)
|
||||
{
|
||||
return (DataTable)CachedData;
|
||||
}
|
||||
}
|
||||
|
||||
// purge cache if command contains "INSERT", "UPDATE", "DELETE", or "ALTER"
|
||||
if (
|
||||
Command.Contains("INSERT", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
Command.Contains("UPDATE", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
Command.Contains("DELETE", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
Command.Contains("ALTER", StringComparison.InvariantCultureIgnoreCase)
|
||||
)
|
||||
{
|
||||
// exclude logging events from purging the cache
|
||||
if (!Command.StartsWith("INSERT INTO SERVERLOGS", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
DatabaseMemoryCache.ClearCache();
|
||||
}
|
||||
}
|
||||
|
||||
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
|
||||
switch (_ConnectorType)
|
||||
{
|
||||
case databaseType.MySql:
|
||||
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
|
||||
return (DataTable)conn.ExecCMD(Command, Parameters, Timeout);
|
||||
DataTable RetTable = conn.ExecCMD(Command, Parameters, Timeout);
|
||||
if (CacheOptions is object && CacheOptions.CacheEnabled)
|
||||
{
|
||||
DatabaseMemoryCache.SetCacheObject(CacheKey, RetTable, CacheOptions.ExpirationSeconds);
|
||||
}
|
||||
return RetTable;
|
||||
default:
|
||||
return new DataTable();
|
||||
}
|
||||
@@ -294,6 +371,148 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
public static class DatabaseMemoryCache
|
||||
{
|
||||
private class MemoryCacheItem
|
||||
{
|
||||
public MemoryCacheItem(object CacheObject)
|
||||
{
|
||||
cacheObject = CacheObject;
|
||||
}
|
||||
|
||||
public MemoryCacheItem(object CacheObject, int ExpirationSeconds)
|
||||
{
|
||||
cacheObject = CacheObject;
|
||||
expirationSeconds = ExpirationSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The time the object was added to the cache in ticks
|
||||
/// </summary>
|
||||
public long addedTime { get; } = Environment.TickCount64;
|
||||
|
||||
/// <summary>
|
||||
/// The time the object will expire in ticks
|
||||
/// </summary>
|
||||
public long expirationTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return addedTime + _expirationTicks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of seconds the object will be cached
|
||||
/// </summary>
|
||||
public int expirationSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)TimeSpan.FromTicks(_expirationTicks).Seconds;
|
||||
}
|
||||
set
|
||||
{
|
||||
_expirationTicks = (long)TimeSpan.FromSeconds(value).Ticks;
|
||||
}
|
||||
}
|
||||
|
||||
private long _expirationTicks = (long)TimeSpan.FromSeconds(2).Ticks;
|
||||
|
||||
/// <summary>
|
||||
/// The object to be cached
|
||||
/// </summary>
|
||||
public object cacheObject { get; set; }
|
||||
}
|
||||
private static Dictionary<string, MemoryCacheItem> MemoryCache = new Dictionary<string, MemoryCacheItem>();
|
||||
private static Timer CacheTimer = new Timer(CacheTimerCallback, null, 0, 1000);
|
||||
|
||||
private static void CacheTimerCallback(object? state)
|
||||
{
|
||||
ClearExpiredCache();
|
||||
}
|
||||
|
||||
public static object? GetCacheObject(string CacheKey)
|
||||
{
|
||||
if (MemoryCache.ContainsKey(CacheKey))
|
||||
{
|
||||
if (MemoryCache[CacheKey].expirationTime < Environment.TickCount)
|
||||
{
|
||||
MemoryCache.Remove(CacheKey);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MemoryCache[CacheKey].cacheObject;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static void SetCacheObject(string CacheKey, object CacheObject, int ExpirationSeconds = 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MemoryCache.ContainsKey(CacheKey))
|
||||
{
|
||||
MemoryCache.Remove(CacheKey);
|
||||
}
|
||||
MemoryCache.Add(CacheKey, new MemoryCacheItem(CacheObject, ExpirationSeconds));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Error while setting cache object", ex);
|
||||
ClearCache();
|
||||
}
|
||||
}
|
||||
public static void RemoveCacheObject(string CacheKey)
|
||||
{
|
||||
if (MemoryCache.ContainsKey(CacheKey))
|
||||
{
|
||||
MemoryCache.Remove(CacheKey);
|
||||
}
|
||||
}
|
||||
public static void ClearCache()
|
||||
{
|
||||
MemoryCache.Clear();
|
||||
}
|
||||
private static void ClearExpiredCache()
|
||||
{
|
||||
try
|
||||
{
|
||||
long currTime = Environment.TickCount64;
|
||||
|
||||
Dictionary<string, MemoryCacheItem> ExpiredItems = MemoryCache;
|
||||
foreach (string key in ExpiredItems.Keys)
|
||||
{
|
||||
if (MemoryCache[key].expirationTime < currTime)
|
||||
{
|
||||
Console.WriteLine("\x1b[95mPurging expired cache item " + key + ". Added: " + MemoryCache[key].addedTime + ". Expired: " + MemoryCache[key].expirationTime);
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Debug, "Database", "Error while clearing expired cache", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseMemoryCacheOptions
|
||||
{
|
||||
public DatabaseMemoryCacheOptions(bool CacheEnabled = false, int ExpirationSeconds = 1)
|
||||
{
|
||||
this.CacheEnabled = CacheEnabled;
|
||||
this.ExpirationSeconds = ExpirationSeconds;
|
||||
}
|
||||
|
||||
public bool CacheEnabled { get; set; }
|
||||
public int ExpirationSeconds { get; set; }
|
||||
}
|
||||
|
||||
public int GetDatabaseSchemaVersion()
|
||||
{
|
||||
switch (_ConnectorType)
|
||||
@@ -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,7 +618,7 @@ 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;
|
||||
|
||||
@@ -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());
|
||||
|
@@ -153,6 +153,35 @@ namespace gaseous_server.Classes
|
||||
// this is a safe background task
|
||||
BackgroundUpgradeTargetSchemaVersions.Add(1022);
|
||||
break;
|
||||
|
||||
case 1023:
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -279,9 +308,10 @@ namespace gaseous_server.Classes
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM 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 +330,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
|
||||
{
|
||||
// no local signature attempt to pull from Hasheous
|
||||
dbSignature = _GetFileSignatureFromHasheous(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
if (dbSignature != null)
|
||||
{
|
||||
// signature retrieved from Hasheous
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + discoveredSignature.Game.Name);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (discoveredSignature == null)
|
||||
{
|
||||
// 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);
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature not found in local database or Hasheous (if enabled) - generating from file data");
|
||||
|
||||
discoveredSignature = dbSignature;
|
||||
}
|
||||
discoveredSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
|
||||
|
||||
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + discoveredSignature.Game.Name);
|
||||
}
|
||||
|
||||
gaseous_server.Models.PlatformMapping.GetIGDBPlatformMapping(ref discoveredSignature, ImageExtension, false);
|
||||
@@ -290,12 +299,6 @@ namespace gaseous_server.Classes
|
||||
MD5 = hash.md5hash,
|
||||
SHA1 = hash.sha1hash
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Import Game", "An error occurred while importing " + ImageName, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (HasheousResult != null)
|
||||
{
|
||||
@@ -351,6 +354,34 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
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, "Get Signature", "Error retrieving signature from Hasheous", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
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);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
foreach (DataRow dr in dbResponse.Rows)
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace gaseous_server.Classes
|
||||
// age groups
|
||||
List<FilterItem> agegroupings = new List<FilterItem>();
|
||||
sql = "SELECT Game.AgeGroupId, COUNT(Game.Id) AS GameCount FROM (SELECT DISTINCT Game.Id, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId WHERE (" + ageRestriction_Platform + ") GROUP BY Game.Id HAVING RomCount > 0) Game GROUP BY Game.AgeGroupId ORDER BY Game.AgeGroupId DESC";
|
||||
dbResponse = db.ExecuteCMD(sql);
|
||||
dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
foreach (DataRow dr in dbResponse.Rows)
|
||||
{
|
||||
@@ -114,7 +114,7 @@ namespace gaseous_server.Classes
|
||||
|
||||
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`;";
|
||||
sql = sql.Replace("<ITEMNAME>", Name);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql);
|
||||
DataTable dbResponse = db.ExecuteCMD(sql, new Database.DatabaseMemoryCacheOptions(CacheEnabled: true, ExpirationSeconds: 300));
|
||||
|
||||
return dbResponse;
|
||||
}
|
||||
|
@@ -13,31 +13,31 @@ namespace gaseous_server
|
||||
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
|
||||
@@ -66,7 +66,7 @@ namespace gaseous_server
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -129,7 +129,7 @@ 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) ||
|
||||
|
@@ -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,6 +70,8 @@ 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";
|
||||
dbDict.Add("md5", hash.md5hash);
|
||||
@@ -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,17 +129,27 @@ 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);
|
||||
|
||||
// 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())
|
||||
{
|
||||
if (biosItem.Available == false && biosItem.hash == hash.md5hash)
|
||||
if (biosItem.Available == false)
|
||||
{
|
||||
if (biosItem.hash == hash.md5hash)
|
||||
{
|
||||
string biosPath = biosItem.biosPath.Replace(biosItem.filename, "");
|
||||
if (!Directory.Exists(biosPath))
|
||||
@@ -142,12 +159,26 @@ namespace gaseous_server.Classes
|
||||
|
||||
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)
|
||||
|
@@ -3,6 +3,7 @@ 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
|
||||
@@ -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)
|
||||
{
|
||||
|
@@ -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 = "";
|
||||
|
@@ -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
|
||||
|
@@ -74,8 +74,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Artwork oldImage = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
||||
|
@@ -195,6 +195,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: Endpoint:" + Endpoint, "IGDB rate limit hit. Pausing API communications for " + RateLimitAvoidanceWait + " milliseconds to avoid rate limiter.");
|
||||
Thread.Sleep(RateLimitAvoidanceWait);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -76,8 +76,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
CompanyLogo oldImage = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
||||
|
@@ -76,8 +76,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Cover oldCover = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
|
||||
if (oldCover.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
||||
|
@@ -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,155 @@ 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
|
||||
Games_Roms.GameId,
|
||||
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
|
||||
Games_Roms
|
||||
LEFT JOIN
|
||||
Platform ON Games_Roms.PlatformId = Platform.Id
|
||||
LEFT JOIN
|
||||
User_RecentPlayedRoms ON User_RecentPlayedRoms.UserId = @userid
|
||||
AND User_RecentPlayedRoms.GameId = Games_Roms.GameId
|
||||
AND User_RecentPlayedRoms.PlatformId = Games_Roms.PlatformId
|
||||
LEFT JOIN
|
||||
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||
AND User_GameFavouriteRoms.GameId = Games_Roms.GameId
|
||||
AND User_GameFavouriteRoms.PlatformId = Games_Roms.PlatformId
|
||||
LEFT JOIN
|
||||
Games_Roms AS GMR ON GMR.Id = User_RecentPlayedRoms.RomId
|
||||
LEFT JOIN
|
||||
Games_Roms AS GFV ON GFV.Id = User_GameFavouriteRoms.RomId
|
||||
WHERE
|
||||
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"];
|
||||
LastPlayedRomName = (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"];
|
||||
FavouriteRomName = (string)row["FavouriteRomName"];
|
||||
}
|
||||
|
||||
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 +674,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 +700,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,8 +76,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
PlatformLogo oldImage = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
||||
|
@@ -13,7 +13,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
}
|
||||
|
||||
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,7 +90,9 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
|
||||
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion, bool GetImages)
|
||||
{
|
||||
if (GetImages == true)
|
||||
{
|
||||
if (platformVersion.PlatformLogo != null)
|
||||
{
|
||||
@@ -104,6 +106,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum SearchUsing
|
||||
{
|
||||
|
@@ -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,6 +133,8 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
if (GetImages == true)
|
||||
{
|
||||
if (platform.PlatformLogo != null)
|
||||
{
|
||||
try
|
||||
@@ -145,6 +147,7 @@ namespace gaseous_server.Classes.Metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPlatformMapping(Platform platform)
|
||||
{
|
||||
|
@@ -74,8 +74,14 @@ namespace gaseous_server.Classes.Metadata
|
||||
{
|
||||
returnValue = await GetObjectFromServer(WhereClause, ImagePath);
|
||||
Storage.NewCacheValue(returnValue, true);
|
||||
|
||||
// check if old value is different from the new value - only download if it's different
|
||||
Screenshot oldImage = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
|
||||
if (oldImage.ImageId != returnValue.ImageId)
|
||||
{
|
||||
forceImageDownload = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
|
||||
|
@@ -175,7 +175,7 @@ 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
|
||||
@@ -428,6 +428,30 @@ 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; }
|
||||
|
@@ -16,7 +16,7 @@ namespace gaseous_server.Classes
|
||||
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);
|
||||
@@ -188,14 +216,34 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -515,7 +551,8 @@ namespace gaseous_server.Classes
|
||||
public long Id { get; set; }
|
||||
public long GameId { get; set; }
|
||||
public long PlatformId { get; set; }
|
||||
public string Platform {
|
||||
public string Platform
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
@@ -528,12 +565,14 @@ namespace gaseous_server.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||
public List<long> RomIds { get; set; }
|
||||
public List<Roms.GameRomItem> Roms { get; set; }
|
||||
public bool HasSaveStates { get; set; } = false;
|
||||
public bool RomUserLastUsed { get; set; }
|
||||
public bool RomUserFavourite { get; set; }
|
||||
private GroupBuildStatus _Status { get; set; }
|
||||
public GroupBuildStatus Status {
|
||||
public GroupBuildStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Status == GroupBuildStatus.Completed)
|
||||
@@ -557,7 +596,8 @@ namespace gaseous_server.Classes
|
||||
_Status = value;
|
||||
}
|
||||
}
|
||||
public long? Size {
|
||||
public long? Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status == GroupBuildStatus.Completed)
|
||||
|
@@ -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();
|
||||
@@ -37,13 +43,34 @@ namespace gaseous_server.Classes
|
||||
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 = Games_Roms.GameId
|
||||
AND User_RecentPlayedRoms.PlatformId = Games_Roms.PlatformId
|
||||
AND User_RecentPlayedRoms.RomId = Games_Roms.Id
|
||||
AND User_RecentPlayedRoms.IsMediaGroup = 0
|
||||
LEFT JOIN
|
||||
User_GameFavouriteRoms ON User_GameFavouriteRoms.UserId = @userid
|
||||
AND User_GameFavouriteRoms.GameId = Games_Roms.GameId
|
||||
AND User_GameFavouriteRoms.PlatformId = Games_Roms.PlatformId
|
||||
AND User_GameFavouriteRoms.RomId = Games_Roms.Id
|
||||
AND User_GameFavouriteRoms.IsMediaGroup = 0
|
||||
";
|
||||
}
|
||||
|
||||
// platform query
|
||||
sqlPlatform = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE GameId = @id ORDER BY Platform.`Name`;";
|
||||
|
||||
if (PlatformId == -1)
|
||||
{
|
||||
// data query
|
||||
sql = "SELECT DISTINCT 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 Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
||||
@@ -51,16 +78,15 @@ namespace gaseous_server.Classes
|
||||
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 Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename, GameState.RomId AS SavedStateRomId" + UserFields + " FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) " + UserJoin + " WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||
|
||||
// count query
|
||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||
|
||||
dbDict.Add("platformid", PlatformId);
|
||||
}
|
||||
DataTable romDT = db.ExecuteCMD(sql, dbDict);
|
||||
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict)[0];
|
||||
DataTable platformDT = db.ExecuteCMD(sqlPlatform, dbDict);
|
||||
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 Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id WHERE Games_Roms.Id = @id";
|
||||
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 Games_Roms.*, Platform.`Name` AS platformname, Game.`Name` AS gamename FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN Game ON Games_Roms.GameId = Game.Id WHERE Games_Roms.MD5 = @id";
|
||||
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(),
|
||||
@@ -242,16 +288,22 @@ namespace gaseous_server.Classes
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return romItem;
|
||||
@@ -267,12 +319,14 @@ 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? 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"]
|
||||
|
187
gaseous-server/Classes/UserProfile.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System.Data;
|
||||
|
||||
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)
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT Id, 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()
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
@@ -188,7 +188,7 @@ 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);
|
||||
@@ -220,6 +220,10 @@ namespace gaseous_server.Controllers
|
||||
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)
|
||||
{
|
||||
@@ -241,6 +245,23 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("Users/Test")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public async Task<IActionResult> TestUserExists(string Email)
|
||||
{
|
||||
ApplicationUser? rawUser = await _userManager.FindByEmailAsync(Email);
|
||||
|
||||
if (rawUser != null)
|
||||
{
|
||||
return Ok(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("Users/{UserId}")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
@@ -256,6 +277,7 @@ 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);
|
||||
@@ -306,7 +328,7 @@ namespace gaseous_server.Controllers
|
||||
// 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);
|
||||
}
|
||||
|
@@ -7,6 +7,9 @@ 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;
|
||||
|
||||
namespace gaseous_server.Controllers
|
||||
{
|
||||
@@ -17,6 +20,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,11 +55,14 @@ 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
|
||||
{
|
||||
if (GameId == -1 || filtered == false)
|
||||
{
|
||||
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
|
||||
|
||||
@@ -67,6 +82,33 @@ namespace gaseous_server.Controllers
|
||||
var stream = new FileStream(tempFile, FileMode.Open);
|
||||
return File(stream, "application/zip", platform.Slug + ".zip");
|
||||
}
|
||||
else
|
||||
{
|
||||
// get user platform map
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
|
||||
PlatformMapping platformMapping = new PlatformMapping();
|
||||
PlatformMapping.PlatformMapItem userPlatformMap = platformMapping.GetUserPlatformMap(user.Id, PlatformId, GameId);
|
||||
|
||||
// build zip file
|
||||
string tempFile = Path.GetTempFileName();
|
||||
|
||||
using (FileStream zipFile = System.IO.File.Create(tempFile))
|
||||
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
|
||||
{
|
||||
foreach (Bios.BiosItem bios in GetBios(PlatformId, true))
|
||||
{
|
||||
if (userPlatformMap.EnabledBIOSHashes.Contains(bios.hash))
|
||||
{
|
||||
zipArchive.CreateEntryFromFile(bios.biosPath, bios.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stream = new FileStream(tempFile, FileMode.Open);
|
||||
return File(stream, "application/zip", userPlatformMap.IGDBSlug + ".zip");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
|
@@ -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
|
||||
{
|
||||
@@ -457,73 +458,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,32 +496,103 @@ 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);
|
||||
|
||||
string? imageId = null;
|
||||
string? imageTypePath = null;
|
||||
|
||||
switch (imageType)
|
||||
{
|
||||
case MetadataImageType.cover:
|
||||
if (gameObject.Cover != null)
|
||||
{
|
||||
if (gameObject.Cover.Id != null)
|
||||
{
|
||||
IGDB.Models.Cover cover = Classes.Metadata.Covers.GetCover(gameObject.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), false);
|
||||
string basePath = Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Game(gameObject), "Covers");
|
||||
imageId = cover.ImageId;
|
||||
imageTypePath = "Covers";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
break;
|
||||
|
||||
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);
|
||||
|
||||
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(basePath, cover.ImageId, size, new List<Communications.IGDBAPI_ImageSize>{ Communications.IGDBAPI_ImageSize.cover_big, Communications.IGDBAPI_ImageSize.original });
|
||||
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 });
|
||||
|
||||
string coverFilePath = ImgFetch.Result;
|
||||
imagePath = ImgFetch.Result;
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(coverFilePath)) {
|
||||
string filename = cover.ImageId + ".jpg";
|
||||
string filepath = coverFilePath;
|
||||
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
|
||||
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
|
||||
@@ -599,10 +604,18 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch
|
||||
@@ -611,6 +624,13 @@ namespace gaseous_server.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public enum MetadataImageType
|
||||
{
|
||||
cover,
|
||||
screenshots,
|
||||
artwork
|
||||
}
|
||||
|
||||
|
||||
[MapToApiVersion("1.0")]
|
||||
[MapToApiVersion("1.1")]
|
||||
@@ -796,7 +816,8 @@ namespace gaseous_server.Controllers
|
||||
companyData.Add("company", company);
|
||||
|
||||
return Ok(companyData);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
@@ -857,17 +878,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
|
||||
{
|
||||
@@ -1025,6 +1175,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]
|
||||
@@ -1137,11 +1348,10 @@ 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);
|
||||
|
||||
@@ -1151,7 +1361,7 @@ namespace gaseous_server.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id));
|
||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id, PlatformId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1388,7 +1598,8 @@ namespace gaseous_server.Controllers
|
||||
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 +1621,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")]
|
||||
|
@@ -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]
|
||||
|
@@ -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,14 +180,22 @@ 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();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return NotFound();
|
||||
|
@@ -31,41 +31,34 @@ 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 (formFile.Length > 0)
|
||||
if (file.Length > 0)
|
||||
{
|
||||
Guid FileId = Guid.NewGuid();
|
||||
|
||||
string filePath = Path.Combine(workPath, Path.GetFileName(formFile.FileName));
|
||||
string filePath = Path.Combine(workPath, Path.GetFileName(file.FileName));
|
||||
|
||||
if (!Directory.Exists(workPath))
|
||||
{
|
||||
Directory.CreateDirectory(workPath);
|
||||
}
|
||||
|
||||
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
||||
|
||||
using (var stream = System.IO.File.Create(filePath))
|
||||
{
|
||||
await formFile.CopyToAsync(stream);
|
||||
await file.CopyToAsync(stream);
|
||||
|
||||
Dictionary<string, object> UploadedFile = new Dictionary<string, object>();
|
||||
UploadedFile.Add("id", FileId.ToString());
|
||||
UploadedFile.Add("originalname", Path.GetFileName(formFile.FileName));
|
||||
UploadedFile.Add("originalname", Path.GetFileName(file.FileName));
|
||||
UploadedFile.Add("fullpath", filePath);
|
||||
UploadedFiles.Add(UploadedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get override platform if specified
|
||||
@@ -75,11 +68,22 @@ namespace gaseous_server.Controllers
|
||||
OverridePlatform = Platforms.GetPlatform((long)OverridePlatformId);
|
||||
}
|
||||
|
||||
// Process uploaded files
|
||||
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
|
||||
{
|
||||
// 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))
|
||||
@@ -87,7 +91,10 @@ namespace gaseous_server.Controllers
|
||||
Directory.Delete(workPath, true);
|
||||
}
|
||||
|
||||
return Ok(new { count = files.Count, size });
|
||||
return Ok(RetVal);
|
||||
}
|
||||
|
||||
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;";
|
||||
@@ -91,7 +91,7 @@ namespace gaseous_server.Controllers
|
||||
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;
|
||||
|
||||
@@ -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,7 +26,7 @@ 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;
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -485,6 +486,7 @@ SELECT DISTINCT
|
||||
Game.`Name`,
|
||||
Game.NameThe,
|
||||
Game.Slug,
|
||||
Game.Summary,
|
||||
Game.PlatformId,
|
||||
Game.TotalRating,
|
||||
Game.TotalRatingCount,
|
||||
@@ -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,28 +595,43 @@ FROM
|
||||
|
||||
// build alpha list
|
||||
Dictionary<string, int> AlphaList = new Dictionary<string, int>();
|
||||
if (orderByField == "NameThe" || orderByField == "Name")
|
||||
{
|
||||
int CurrentPage = 1;
|
||||
int NextPageIndex = pageSize;
|
||||
for (int i = 0; i < dbResponse.Rows.Count; i++)
|
||||
|
||||
string alphaSearchField;
|
||||
if (orderByField == "NameThe")
|
||||
{
|
||||
string firstChar = dbResponse.Rows[i]["NameThe"].ToString().Substring(0, 1).ToUpperInvariant();
|
||||
if (!"ABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(firstChar))
|
||||
{
|
||||
if (!AlphaList.ContainsKey("#"))
|
||||
{
|
||||
AlphaList.Add("#", 1);
|
||||
}
|
||||
alphaSearchField = "NameThe";
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (NextPageIndex == i + 1)
|
||||
}
|
||||
else
|
||||
{
|
||||
NextPageIndex += pageSize;
|
||||
CurrentPage += 1;
|
||||
if (!AlphaList.ContainsKey("#"))
|
||||
{
|
||||
AlphaList.Add("#", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ using Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Data;
|
||||
using Asp.Versioning;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace gaseous_server.Controllers.v1_1
|
||||
{
|
||||
@@ -13,7 +14,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;
|
||||
@@ -233,11 +234,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 },
|
||||
@@ -254,7 +255,9 @@ namespace gaseous_server.Controllers.v1_1
|
||||
}
|
||||
else
|
||||
{
|
||||
string filename = "savestate.state";
|
||||
// get rom data
|
||||
Roms.GameRomItem romItem = Roms.GetRom(RomId);
|
||||
|
||||
byte[] bytes;
|
||||
if ((bool)data.Rows[0]["Zipped"] == false)
|
||||
{
|
||||
@@ -264,7 +267,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(romItem.Name);
|
||||
|
||||
|
||||
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", romItem.Name },
|
||||
{ "StateDateTime", data.Rows[0]["StateDateTime"] },
|
||||
{ "StateName", data.Rows[0]["Name"] }
|
||||
};
|
||||
if ((int)data.Rows[0]["IsMediaGroup"] == 0)
|
||||
{
|
||||
RomInfo.Add("MD5", romItem.Md5);
|
||||
RomInfo.Add("SHA1", romItem.Sha1);
|
||||
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 +360,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 = "5Minute")]
|
||||
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; }
|
||||
}
|
||||
}
|
@@ -15,8 +15,6 @@ namespace gaseous_server.Models
|
||||
{
|
||||
public class PlatformMapping
|
||||
{
|
||||
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
|
||||
|
||||
/// <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
|
||||
{
|
||||
@@ -93,49 +92,57 @@ 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 = PlatformMapCache[mapId.ToString()];
|
||||
if (mapItem != null)
|
||||
{
|
||||
platformMaps.Add(mapItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
PlatformMapItem mapItem = BuildPlatformMapItem(row);
|
||||
if (mapItem != null)
|
||||
{
|
||||
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()))
|
||||
{
|
||||
return PlatformMapCache[Id.ToString()];
|
||||
}
|
||||
else
|
||||
{
|
||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
|
||||
@@ -156,10 +163,11 @@ namespace gaseous_server.Models
|
||||
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,6 +481,74 @@ 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; }
|
||||
@@ -502,6 +596,15 @@ 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; }
|
||||
}
|
||||
}
|
17
gaseous-server/Models/UserProfile.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace gaseous_server.Models
|
||||
{
|
||||
public class UserProfile
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Quip { 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();
|
||||
@@ -71,7 +74,7 @@ ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
|
||||
1,
|
||||
new List<ProcessQueue.QueueItemType>
|
||||
{
|
||||
ProcessQueue.QueueItemType.All
|
||||
ProcessQueue.QueueItemType.SignatureIngestor
|
||||
},
|
||||
false,
|
||||
true
|
||||
@@ -398,9 +401,6 @@ gaseous_server.Classes.Metadata.Platforms.AssignAllPlatformsToGameIdZero();
|
||||
// extract platform map if not present
|
||||
PlatformMapping.ExtractPlatformMap();
|
||||
|
||||
// force load platform map into cache
|
||||
var platformMap = PlatformMapping.PlatformMap;
|
||||
|
||||
// add background tasks
|
||||
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
|
||||
ProcessQueue.QueueItemType.SignatureIngestor)
|
||||
|
@@ -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);
|
97
gaseous-server/Support/Database/MySQL/gaseous-1023.sql
Normal file
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
|
||||
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
|
||||
);
|
@@ -67,6 +67,7 @@
|
||||
<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="Classes\Metadata\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -114,5 +115,6 @@
|
||||
<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" />
|
||||
</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 |
9
gaseous-server/wwwroot/images/pending.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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 14 14" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g fill-rule="evenodd">
|
||||
|
||||
<path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/>
|
||||
|
After Width: | Height: | Size: 576 B |
54
gaseous-server/wwwroot/images/processing.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 33.132 33.132" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M33.132,30.941l-6.096-5.439c1.151-1.459,1.843-3.297,1.843-5.297c0-4.453-3.414-8.107-7.761-8.525
|
||||
c-0.061-0.18-0.127-0.355-0.232-0.537l-0.148-0.258c0.339-0.482,0.529-0.996,0.529-1.535V6.289c0-0.416-0.116-0.838-0.347-1.256
|
||||
l-0.07-0.131c0.095-0.221,0.161-0.447,0.161-0.688c0-2.472-5.35-3.81-10.379-3.81c-5.03,0-10.375,1.338-10.375,3.811
|
||||
c0,0.24,0.064,0.467,0.16,0.688L0.345,5.037C0.114,5.451,0,5.873,0,6.289V9.35c0,0.539,0.188,1.053,0.528,1.535l-0.15,0.262
|
||||
C0.126,11.578,0,12.019,0,12.455v3.062c0,0.525,0.179,1.029,0.502,1.502l-0.124,0.213C0.126,17.666,0,18.107,0,18.539v3.066
|
||||
c0,2.721,4.669,4.85,10.632,4.85c1.221,0,2.383-0.092,3.465-0.26c-0.068-0.09-0.123-0.193-0.188-0.291
|
||||
c1.571,1.762,3.846,2.881,6.389,2.881c2.076,0,3.984-0.74,5.47-1.973l5.431,5.916L33.132,30.941z M16.665,16.543
|
||||
c2.088-2.074,5.455-2.061,7.523,0.02c2.073,2.086,2.059,5.459-0.024,7.525c-2.087,2.072-5.454,2.062-7.528-0.023
|
||||
C14.565,21.98,14.583,18.611,16.665,16.543z M10.632,1.545c5.723,0,9.24,1.555,9.24,2.67c0,1.117-3.518,2.672-9.24,2.672
|
||||
c-5.726,0-9.238-1.555-9.238-2.672C1.394,3.099,4.906,1.545,10.632,1.545z M1.14,6.802c1.705,1.445,5.66,2.193,9.492,2.193
|
||||
c3.834,0,7.787-0.748,9.494-2.193v2.547c0,1.756-3.896,3.713-9.494,3.713c-5.594,0-9.492-1.957-9.492-3.713
|
||||
C1.14,9.349,1.14,6.802,1.14,6.802z M1.14,12.636c1.744,1.6,5.336,2.621,9.492,2.621c0.99,0,1.926-0.078,2.829-0.191
|
||||
c-0.695,0.926-1.224,1.986-1.501,3.146c-0.077,0.316-0.136,0.639-0.176,0.969c-0.382,0.018-0.754,0.049-1.152,0.049
|
||||
c-5.594,0-9.492-1.957-9.492-3.713C1.14,15.517,1.14,12.636,1.14,12.636z M10.632,25.318c-5.594,0-9.492-1.957-9.492-3.713v-2.881
|
||||
c1.744,1.6,5.336,2.619,9.492,2.619c0.396,0,0.779-0.025,1.164-0.049c0.133,1.045,0.447,2.029,0.922,2.918
|
||||
c0.173,0.332,0.38,0.641,0.594,0.941C12.474,25.258,11.579,25.318,10.632,25.318z"/>
|
||||
<path d="M24.4,19.961l0.379-1.129c-2.979-4.801-7.113-2.014-7.113-2.014C22.841,15.881,24.4,19.961,24.4,19.961z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
70
gaseous-server/wwwroot/images/recent.svg
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
version="1.1"
|
||||
id="svg822"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)"
|
||||
sodipodi:docname="recent.svg">
|
||||
<defs
|
||||
id="defs816" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="17.833333"
|
||||
inkscape:cx="15"
|
||||
inkscape:cy="15"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="713"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid816" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata819">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>
|
||||
|
||||
</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-289.0625)">
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 15 3 C 8.3844276 3 3 8.38443 3 15 C 3 21.61557 8.3844276 27 15 27 C 21.615572 27 27 21.61557 27 15 C 27 8.38443 21.615572 3 15 3 z M 15 5 C 20.534692 5 25 9.46531 25 15 C 25 20.53469 20.534692 25 15 25 C 9.4653079 25 5 20.53469 5 15 C 5 9.46531 9.4653079 5 15 5 z M 15 7 C 14.446 7 14 7.446 14 8 L 14 15 C 14 15.554 14.446 16 15 16 L 22 16 C 22.554 16 23 15.554 23 15 C 23 14.446 22.554 14 22 14 L 16 14 L 16 8 C 16 7.446 15.554 7 15 7 z "
|
||||
transform="translate(0,289.0625)"
|
||||
id="path1099" />
|
||||
</g>
|
After Width: | Height: | Size: 3.5 KiB |
15
gaseous-server/wwwroot/images/start-task.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="800px" height="800px" viewBox="0 0 46 46" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M20.913,34.756c-0.625,0.627-1.565,0.816-2.383,0.479c-0.818-0.336-1.352-1.135-1.352-2.02V12.784
|
||||
c0-0.885,0.534-1.683,1.352-2.021c0.818-0.338,1.759-0.149,2.383,0.479l9.657,9.706c1.129,1.134,1.129,2.967,0,4.102
|
||||
L20.913,34.756z"/>
|
||||
<path d="M23,6c9.374,0,17,7.626,17,17s-7.626,17-17,17S6,32.374,6,23S13.626,6,23,6 M23,0C10.298,0,0,10.297,0,23s10.298,23,23,23
|
||||
s23-10.297,23-23S35.702,0,23,0L23,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 878 B |
@@ -1,5 +1,5 @@
|
||||
<?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 16 16" xmlns="http://www.w3.org/2000/svg" version="1.1" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
|
||||
<path d="m14.25 8.75c-.5 2.5-2.3849 4.85363-5.03069 5.37991-2.64578.5263-5.33066-.7044-6.65903-3.0523-1.32837-2.34784-1.00043-5.28307.81336-7.27989 1.81379-1.99683 4.87636-2.54771 7.37636-1.54771"/>
|
||||
<polyline points="5.75 7.75,8.25 10.25,14.25 3.75"/>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 22C17.5 22 22 17.5 22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22Z" stroke="green" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.75 12L10.58 14.83L16.25 9.17004" stroke="green" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 538 B |
12
gaseous-server/wwwroot/images/viewicon.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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="M3 1C1.89543 1 1 1.89543 1 3V5C1 6.10457 1.89543 7 3 7H5C6.10457 7 7 6.10457 7 5V3C7 1.89543 6.10457 1 5 1H3ZM5 3H3L3 5H5V3Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 9C1.89543 9 1 9.89543 1 11V13C1 14.1046 1.89543 15 3 15H5C6.10457 15 7 14.1046 7 13V11C7 9.89543 6.10457 9 5 9H3ZM5 11H3L3 13H5V11Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 19C1 17.8954 1.89543 17 3 17H5C6.10457 17 7 17.8954 7 19V21C7 22.1046 6.10457 23 5 23H3C1.89543 23 1 22.1046 1 21V19ZM3 19H5V21H3L3 19Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 1C9.89543 1 9 1.89543 9 3V5C9 6.10457 9.89543 7 11 7H13C14.1046 7 15 6.10457 15 5V3C15 1.89543 14.1046 1 13 1H11ZM13 3H11V5H13V3Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 11C9 9.89543 9.89543 9 11 9H13C14.1046 9 15 9.89543 15 11V13C15 14.1046 14.1046 15 13 15H11C9.89543 15 9 14.1046 9 13V11ZM11 11H13V13H11V11Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 17C9.89543 17 9 17.8954 9 19V21C9 22.1046 9.89543 23 11 23H13C14.1046 23 15 22.1046 15 21V19C15 17.8954 14.1046 17 13 17H11ZM13 19H11V21H13V19Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 3C17 1.89543 17.8954 1 19 1H21C22.1046 1 23 1.89543 23 3V5C23 6.10457 22.1046 7 21 7H19C17.8954 7 17 6.10457 17 5V3ZM19 3H21V5H19V3Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19 9C17.8954 9 17 9.89543 17 11V13C17 14.1046 17.8954 15 19 15H21C22.1046 15 23 14.1046 23 13V11C23 9.89543 22.1046 9 21 9H19ZM21 11H19V13H21V11Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 19C17 17.8954 17.8954 17 19 17H21C22.1046 17 23 17.8954 23 19V21C23 22.1046 22.1046 23 21 23H19C17.8954 23 17 22.1046 17 21V19ZM19 19H21V21H19V19Z" fill="#0F0F0F"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
8
gaseous-server/wwwroot/images/viewinfinite.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#2e3436">
|
||||
<path d="m 5.5 0 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 5 c 0.277344 0 0.5 -0.222656 0.5 -0.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 3 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 5 c 0.277344 0 0.5 -0.222656 0.5 -0.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 8 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 5 c 0.277344 0 0.5 -0.222656 0.5 -0.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 3 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 5 c 0.277344 0 0.5 -0.222656 0.5 -0.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 z m 0 0" fill-opacity="0.34902"/>
|
||||
<path d="m 15 7 h -14 v 2 h 14 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1023 B |
7
gaseous-server/wwwroot/images/viewlist.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?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="M9 6C9 4.34315 7.65685 3 6 3H4C2.34315 3 1 4.34315 1 6V8C1 9.65685 2.34315 11 4 11H6C7.65685 11 9 9.65685 9 8V6ZM7 6C7 5.44772 6.55228 5 6 5H4C3.44772 5 3 5.44772 3 6V8C3 8.55228 3.44772 9 4 9H6C6.55228 9 7 8.55228 7 8V6Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 16C9 14.3431 7.65685 13 6 13H4C2.34315 13 1 14.3431 1 16V18C1 19.6569 2.34315 21 4 21H6C7.65685 21 9 19.6569 9 18V16ZM7 16C7 15.4477 6.55228 15 6 15H4C3.44772 15 3 15.4477 3 16V18C3 18.5523 3.44772 19 4 19H6C6.55228 19 7 18.5523 7 18V16Z" fill="#0F0F0F"/>
|
||||
<path d="M11 7C11 6.44772 11.4477 6 12 6H22C22.5523 6 23 6.44772 23 7C23 7.55228 22.5523 8 22 8H12C11.4477 8 11 7.55228 11 7Z" fill="#0F0F0F"/>
|
||||
<path d="M11 17C11 16.4477 11.4477 16 12 16H22C22.5523 16 23 16.4477 23 17C23 17.5523 22.5523 18 22 18H12C11.4477 18 11 17.5523 11 17Z" fill="#0F0F0F"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
9
gaseous-server/wwwroot/images/viewpaged.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#2e3436">
|
||||
<path d="m 9 5.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 h -3 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 3 c 0.277344 0 0.5 -0.222656 0.5 -0.5 z m 0 0" fill-opacity="0.34902"/>
|
||||
<path d="m 4 0 c -1.644531 0 -3 1.355469 -3 3 v 7 c 0 1.644531 1.355469 3 3 3 h 6 c 1.644531 0 3 -1.355469 3 -3 v -7 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 h 6 c 0.570312 0 1 0.429688 1 1 v 7 c 0 0.570312 -0.429688 1 -1 1 h -6 c -0.570312 0 -1 -0.429688 -1 -1 v -7 c 0 -0.570312 0.429688 -1 1 -1 z m 11 2 c -0.550781 0 -1 0.449219 -1 1 v 8 c 0 0.550781 -0.449219 1 -1 1 h -7 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 7 c 1.65625 0 3 -1.34375 3 -3 v -8 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0"/>
|
||||
<path d="m 9 8.5 v -1 c 0 -0.277344 -0.222656 -0.5 -0.5 -0.5 h -3 c -0.277344 0 -0.5 0.222656 -0.5 0.5 v 1 c 0 0.277344 0.222656 0.5 0.5 0.5 h 3 c 0.277344 0 0.5 -0.222656 0.5 -0.5 z m 0 0" fill-opacity="0.34902"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,23 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="/api/v1.1/System/VersionFile"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/styles/style.css" dat-href="/styles/style.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/styles/notifications.css" dat-href="/styles/notifications.css" />
|
||||
<script src="/scripts/jquery-3.6.0.min.js"></script>
|
||||
<script src="/scripts/moment-with-locales.min.js"></script>
|
||||
<link href="/styles/select2.min.css" rel="stylesheet" />
|
||||
<link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" />
|
||||
<script src="/scripts/jquery.lazy.min.js"></script>
|
||||
<script src="/scripts/jquery.lazy.plugins.min.js"></script>
|
||||
<script src="/scripts/select2.min.js"></script>
|
||||
<script src="/scripts/dropzone.min.js"></script>
|
||||
<script src="/scripts/simpleUpload.min.js"></script>
|
||||
<script src="/scripts/main.js" type="text/javascript"></script>
|
||||
<script src="/scripts/notifications.js" type="text/javascript"></script>
|
||||
<script src="/scripts/filterformating.js" type="text/javascript"></script>
|
||||
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
@@ -25,165 +11,138 @@
|
||||
<title>Gaseous Games</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
// update src links to ensure the latest versions are always loaded.
|
||||
|
||||
// update links
|
||||
var headLinks = document.getElementsByTagName('link');
|
||||
for (var i = 0; i < headLinks.length; i++) {
|
||||
if (headLinks[i].getAttribute('dat-href') && headLinks[i].rel == "stylesheet") {
|
||||
var newLink = document.createElement('link');
|
||||
let styleSheets = [
|
||||
"/styles/select2.min.css",
|
||||
"/styles/stylevars.css",
|
||||
"/styles/style.css",
|
||||
"/styles/notifications.css"
|
||||
];
|
||||
|
||||
let scriptLinks = [
|
||||
"/scripts/jquery-3.6.0.min.js",
|
||||
"/scripts/jquery.lazy.min.js",
|
||||
"/scripts/jquery.lazy.plugins.min.js",
|
||||
"/scripts/moment-with-locales.min.js",
|
||||
"/scripts/select2.min.js",
|
||||
"/scripts/modals.js",
|
||||
"/scripts/preferences.js",
|
||||
"/scripts/account.js",
|
||||
"/scripts/libraries.js",
|
||||
"/scripts/notifications.js",
|
||||
"/scripts/rominfo.js",
|
||||
"/scripts/uploadrom.js",
|
||||
"/scripts/filterformating.js",
|
||||
"/scripts/gamesformating.js",
|
||||
"/scripts/main.js"
|
||||
];
|
||||
|
||||
let head = document.getElementsByTagName('head')[0];
|
||||
|
||||
// placeholder for global userProfile variable
|
||||
var userProfile;
|
||||
|
||||
// update script links
|
||||
for (let i = 0; i < scriptLinks.length; i++) {
|
||||
let newScript = document.createElement('script');
|
||||
newScript.src = scriptLinks[i] + '?v=' + AppVersion;
|
||||
newScript.type = "text/javascript";
|
||||
newScript.async = false;
|
||||
|
||||
head.appendChild(newScript);
|
||||
}
|
||||
|
||||
// update stylesheet links
|
||||
for (let i = 0; i < styleSheets.length; i++) {
|
||||
let newLink = document.createElement('link');
|
||||
newLink.rel = "stylesheet";
|
||||
newLink.href = headLinks[i].getAttribute('dat-href') + '?v=' + AppVersion;
|
||||
newLink.href = styleSheets[i] + '?v=' + AppVersion;
|
||||
newLink.type = "text/css";
|
||||
|
||||
headLinks[i].parentElement.removeChild(headLinks[i]);
|
||||
head.appendChild(newLink);
|
||||
}
|
||||
|
||||
async function LoadPageContent(page, targetDiv) {
|
||||
if (targetDiv == undefined || targetDiv == null || targetDiv == '') {
|
||||
targetDiv = 'content';
|
||||
}
|
||||
|
||||
var userProfile;
|
||||
// load page content
|
||||
let pageContentResponse = await fetch('/pages/' + page + '.html' + '?v=' + AppVersion);
|
||||
let pageContentContent = await pageContentResponse.text();
|
||||
document.getElementById(targetDiv).innerHTML = pageContentContent;
|
||||
|
||||
// load page script
|
||||
let pageScriptLink = '/pages/' + page + '.js';
|
||||
let script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = pageScriptLink + '?v=' + AppVersion;
|
||||
script.async = false;
|
||||
document.head.appendChild(script);
|
||||
|
||||
backgroundImageHandler = new BackgroundImageRotator();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Background Images -->
|
||||
<div id="bgImages"></div>
|
||||
<div id="bgImage_Opacity"></div>
|
||||
|
||||
<!-- Notifications -->
|
||||
<div id="notifications_target"></div>
|
||||
|
||||
<div id="banner_icon" onclick="window.location.href = '/index.html';">
|
||||
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
|
||||
</div>
|
||||
<div id="banner_header">
|
||||
<div id="bannerButtons">
|
||||
<div id="banner_user" onclick="showMenu();" class="banner_button dropdown dropbtn">
|
||||
<img src="/images/user.svg" alt="Account" title="Account" id="banner_user_image" class="banner_button_image" style="position: relative; top: 10px; right: 0px; pointer-events: none;" onclick="showMenu();" />
|
||||
<div id="myDropdown" class="dropdown-content">
|
||||
<div id="banner_user_roles"></div>
|
||||
<a href="#" onclick="showDialog('userprofile');">Profile</a>
|
||||
<a href="#" onclick="userLogoff();">Sign Out</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page Banner -->
|
||||
<div id="banner_target"></div>
|
||||
|
||||
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';" class="banner_button">
|
||||
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" class="banner_button_image" />
|
||||
<span id="banner_system_label">Settings</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_upload" onclick="showDialog('upload');" class="banner_button">
|
||||
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" class="banner_button_image" />
|
||||
<span id="banner_upload_label">Upload</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';" class="banner_button">
|
||||
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image" class="banner_button_image" />
|
||||
<span id="banner_collections_label">Collections</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_library" onclick="window.location.href = '/index.html';" class="banner_button">
|
||||
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image" class="banner_button_image" />
|
||||
<span id="banner_library_label">Library</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="content">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="myModal" class="modal">
|
||||
|
||||
<!-- Modal content -->
|
||||
<div class="modal-content">
|
||||
<span class="close">×</span>
|
||||
<div><h1 id="modal-heading">Modal heading</h1></div>
|
||||
<div id="modal-content">Some text in the Modal..</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="myModalSub" class="modal">
|
||||
|
||||
<!-- Modal content -->
|
||||
<div class="modal-content-sub">
|
||||
<span id="modal-close-sub" class="close">×</span>
|
||||
<div id="modal-content-sub">Some text in the Modal..</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Page Content -->
|
||||
<div id="content"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var modalVariables = null;
|
||||
// start the application
|
||||
let backgroundImageHandler = undefined;
|
||||
console.log("Starting Gaseous Games");
|
||||
console.log("App Version: " + AppVersion);
|
||||
console.log("First Run Status: " + FirstRunStatus);
|
||||
switch (FirstRunStatus) {
|
||||
case 0:
|
||||
case "0":
|
||||
// first run - load first run wizard
|
||||
LoadPageContent('first', 'content');
|
||||
break;
|
||||
|
||||
// redirect if first run status = 0
|
||||
if (FirstRunStatus == 0) {
|
||||
window.location.replace("/pages/first.html");
|
||||
}
|
||||
default:
|
||||
// first run - load login page or redirect if user already logged in
|
||||
|
||||
// redirect if not logged in
|
||||
ajaxCall(
|
||||
'/api/v1.1/Account/Profile/Basic',
|
||||
'GET',
|
||||
function(result) {
|
||||
fetch('/api/v1.1/Account/Profile/Basic')
|
||||
.then(async response => {
|
||||
if (response.ok) {
|
||||
// user is signed in - start setting up the application
|
||||
console.log("User is logged in");
|
||||
userProfile = result;
|
||||
userProfile = await response.json();
|
||||
|
||||
loadAvatar(userProfile.avatar);
|
||||
// load page banner
|
||||
LoadPageContent('banner', 'banner_target');
|
||||
|
||||
// hide the upload button if it's not permitted
|
||||
var uploadButton = document.getElementById('banner_upload');
|
||||
if (!userProfile.roles.includes("Admin") && !userProfile.roles.includes("Gamer")) {
|
||||
uploadButton.style.display = 'none';
|
||||
}
|
||||
// load page content
|
||||
let pageSelection = getQueryString('page', 'string');
|
||||
|
||||
// populate page
|
||||
var myParam = getQueryString('page', 'string');
|
||||
|
||||
if (!myParam) {
|
||||
myParam = 'home';
|
||||
if (!pageSelection) {
|
||||
pageSelection = GetPreference("DefaultHomePage", 'home');
|
||||
}
|
||||
|
||||
$('#content').load('/pages/' + myParam + '.html?v=' + AppVersion);
|
||||
},
|
||||
function(error) {
|
||||
window.location.replace("/pages/login.html");
|
||||
LoadPageContent(pageSelection, 'content');
|
||||
} else {
|
||||
// user is not signed in - load login page
|
||||
LoadPageContent('login');
|
||||
}
|
||||
);
|
||||
|
||||
/* When the user clicks on the button,
|
||||
toggle between hiding and showing the dropdown content */
|
||||
function showMenu() {
|
||||
document.getElementById("myDropdown").classList.toggle("show");
|
||||
}
|
||||
|
||||
// Close the dropdown menu if the user clicks outside of it
|
||||
window.onclick = function(event) {
|
||||
if (!event.target.matches('.dropbtn')) {
|
||||
var dropdowns = document.getElementsByClassName("dropdown-content");
|
||||
var i;
|
||||
for (i = 0; i < dropdowns.length; i++) {
|
||||
var openDropdown = dropdowns[i];
|
||||
if (openDropdown.classList.contains('show')) {
|
||||
openDropdown.classList.remove('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function userLogoff() {
|
||||
ajaxCall(
|
||||
'/api/v1.1/Account/LogOff',
|
||||
'POST',
|
||||
function (result) {
|
||||
location.replace("/index.html");
|
||||
},
|
||||
function (error) {
|
||||
location.replace("/index.html");
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
LoadPageContent('login');
|
||||
});
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
53
gaseous-server/wwwroot/pages/banner.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<div id="banner_icon" onclick="window.location.href = '/index.html';">
|
||||
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
|
||||
</div>
|
||||
<div id="banner_header">
|
||||
<div id="bannerButtons">
|
||||
<div id="banner_user" class="banner_button dropdown dropbtn">
|
||||
<div id="banner_user_image_box"></div>
|
||||
<div id="myDropdown" class="dropdown-content">
|
||||
<div id="banner_user_profilecard"></div>
|
||||
<a href="#" id="dropdown-menu-account">Profile and Account</a>
|
||||
<a href="#" id="dropdown-menu-preferences">Preferences</a>
|
||||
<div class="dropdown-menu-separator"></div>
|
||||
<a id="banner_user_logoff" href="#">Sign Out</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="banner_cog" class="banner_button">
|
||||
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image"
|
||||
class="banner_button_image" />
|
||||
<span id="banner_system_label">Settings</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_upload" class="banner_button">
|
||||
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image"
|
||||
class="banner_button_image" />
|
||||
<span id="banner_upload_label">Upload</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_collections" class="banner_button">
|
||||
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image"
|
||||
class="banner_button_image" />
|
||||
<span id="banner_collections_label">Collections</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_library" class="banner_button">
|
||||
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image"
|
||||
class="banner_button_image" />
|
||||
<span id="banner_library_label">Library</span>
|
||||
</div>
|
||||
|
||||
<div id="banner_home" class="banner_button">
|
||||
<img src="/images/home.svg" alt="Home" title="Home" id="banner_home_image" class="banner_button_image" />
|
||||
<span id="banner_home_label">Home</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="banner_header_label" onclick="window.location.href = '/index.html';">Gaseous Games</div>
|
||||
</div>
|
||||
|
||||
<!-- page code -->
|
||||
<script type="text/javascript">
|
||||
setupBanner();
|
||||
</script>
|
103
gaseous-server/wwwroot/pages/banner.js
Normal file
@@ -0,0 +1,103 @@
|
||||
function setupBanner() {
|
||||
// attach event listeners to the banner elements
|
||||
let userMenu = document.getElementById("banner_user");
|
||||
if (userMenu) {
|
||||
userMenu.addEventListener('click', () => {
|
||||
document.getElementById("myDropdown").classList.toggle("show");
|
||||
});
|
||||
}
|
||||
|
||||
let userMenuLogoff = document.getElementById("banner_user_logoff");
|
||||
if (userMenuLogoff) {
|
||||
userMenuLogoff.addEventListener('click', () => {
|
||||
ajaxCall(
|
||||
'/api/v1.1/Account/LogOff',
|
||||
'POST',
|
||||
function (result) {
|
||||
location.replace("/index.html");
|
||||
},
|
||||
function (error) {
|
||||
location.replace("/index.html");
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let bannerCog = document.getElementById("banner_cog");
|
||||
if (bannerCog) {
|
||||
bannerCog.addEventListener('click', () => {
|
||||
window.location.href = '/index.html?page=settings';
|
||||
});
|
||||
}
|
||||
|
||||
let bannerUpload = document.getElementById("banner_upload");
|
||||
if (bannerUpload) {
|
||||
bannerUpload.addEventListener('click', () => {
|
||||
const uploadDialog = new UploadRom();
|
||||
uploadDialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
let bannerCollection = document.getElementById("banner_collections");
|
||||
if (bannerCollection) {
|
||||
bannerCollection.addEventListener('click', () => {
|
||||
window.location.href = '/index.html?page=collections';
|
||||
});
|
||||
}
|
||||
|
||||
let bannerLibrary = document.getElementById("banner_library");
|
||||
if (bannerLibrary) {
|
||||
bannerLibrary.addEventListener('click', () => {
|
||||
window.location.href = '/index.html?page=library';
|
||||
});
|
||||
}
|
||||
|
||||
let bannerHome = document.getElementById("banner_home");
|
||||
if (bannerHome) {
|
||||
bannerHome.addEventListener('click', () => {
|
||||
window.location.href = '/index.html?page=home';
|
||||
});
|
||||
}
|
||||
|
||||
// set avatar
|
||||
let avatarBox = document.getElementById('banner_user_image_box');
|
||||
let avatar = new Avatar(userProfile.profileId, 30, 30);
|
||||
avatarBox.style = 'pointer-events: none;';
|
||||
avatar.setAttribute('style', 'margin-top: 5px; pointer-events: none; width: 30px; height: 30px;');
|
||||
avatarBox.appendChild(avatar);
|
||||
|
||||
// set profile card in drop down
|
||||
let profileCard = document.getElementById('banner_user_profilecard');
|
||||
let profileCardContent = new ProfileCard(userProfile.profileId, true);
|
||||
profileCard.appendChild(profileCardContent);
|
||||
|
||||
// hide the upload button if it's not permitted
|
||||
let uploadButton = document.getElementById('banner_upload');
|
||||
if (!userProfile.roles.includes("Admin") && !userProfile.roles.includes("Gamer")) {
|
||||
uploadButton.style.display = 'none';
|
||||
}
|
||||
|
||||
// Close the dropdown menu if the user clicks outside of it
|
||||
window.onclick = function (event) {
|
||||
if (!event.target.matches('.dropbtn')) {
|
||||
let dropdowns = document.getElementsByClassName("dropdown-content");
|
||||
for (let i = 0; i < dropdowns.length; i++) {
|
||||
let openDropdown = dropdowns[i];
|
||||
if (openDropdown.classList.contains('show')) {
|
||||
openDropdown.classList.remove('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// event for preferences drop down item
|
||||
document.getElementById('dropdown-menu-preferences').addEventListener('click', function () {
|
||||
const prefsDialog = new PreferencesWindow();
|
||||
prefsDialog.open();
|
||||
});
|
||||
// event for account drop down item
|
||||
document.getElementById('dropdown-menu-account').addEventListener('click', function () {
|
||||
const accountDialog = new AccountWindow(); accountDialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
setupBanner();
|
@@ -1,4 +1,5 @@
|
||||
<div id="bgImage" style="background-image: url('/images/CollectionsWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
|
||||
<div id="bgImage"
|
||||
style="background-image: url('/images/CollectionsWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
|
||||
<div id="bgImage_Opacity"></div>
|
||||
</div>
|
||||
|
||||
@@ -12,88 +13,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settings_photocredit">
|
||||
Wallpaper by <a href="https://wallpapercave.com/u/andrea16" class="romlink">andrea16</a> / <a href="https://wallpapercave.com/w/wp5206111" class="romlink">Wallpaper Cave</a>
|
||||
</div>
|
||||
<!-- <div id="settings_photocredit">
|
||||
Wallpaper by <a href="https://wallpapercave.com/u/andrea16" class="romlink">andrea16</a> / <a
|
||||
href="https://wallpapercave.com/w/wp5206111" class="romlink">Wallpaper Cave</a>
|
||||
</div> -->
|
||||
|
||||
<script type="text/javascript">
|
||||
var newCollectionButton = document.getElementById('collection_new');
|
||||
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
|
||||
newCollectionButton.style.display = '';
|
||||
} else {
|
||||
newCollectionButton.style.display = 'none';
|
||||
}
|
||||
|
||||
GetCollections();
|
||||
|
||||
function GetCollections() {
|
||||
ajaxCall('/api/v1.1/Collections', 'GET', function (result) {
|
||||
if (result) {
|
||||
var targetDiv = document.getElementById('collection_table_location');
|
||||
targetDiv.innerHTML = '';
|
||||
|
||||
var newTable = document.createElement('table');
|
||||
newTable.id = 'romtable';
|
||||
newTable.className = 'romtable';
|
||||
newTable.setAttribute('cellspacing', 0);
|
||||
newTable.appendChild(createTableRow(true, [ 'Name', 'Description', 'Download Status', 'Size', '' ]));
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
var statusText = result[i].buildStatus;
|
||||
var downloadLink = '';
|
||||
var packageSize = '-';
|
||||
var inProgress = false;
|
||||
switch (result[i].buildStatus) {
|
||||
case 'NoStatus':
|
||||
statusText = '-';
|
||||
break;
|
||||
case "WaitingForBuild":
|
||||
statusText = 'Build pending';
|
||||
inProgress = true;
|
||||
break;
|
||||
case "Building":
|
||||
statusText = 'Building';
|
||||
inProgress = true;
|
||||
break;
|
||||
case "Completed":
|
||||
statusText = 'Available';
|
||||
downloadLink = '<a href="/api/v1.1/Collections/' + result[i].id + '/Roms/Zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
|
||||
packageSize = formatBytes(result[i].collectionBuiltSizeBytes);
|
||||
break;
|
||||
case "Failed":
|
||||
statusText = 'Build error';
|
||||
break;
|
||||
default:
|
||||
statusText = result[i].buildStatus;
|
||||
break;
|
||||
}
|
||||
|
||||
if (inProgress == true) {
|
||||
setTimeout(GetCollections, 10000);
|
||||
}
|
||||
|
||||
var editButton = '';
|
||||
var deleteButton = '';
|
||||
|
||||
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
|
||||
editButton = '<a href="#" onclick="showDialog(\'collectionedit\', ' + result[i].id + ');" class="romlink"><img src="/images/edit.svg" class="banner_button_image" alt="Edit" title="Edit" /></a>';
|
||||
|
||||
deleteButton = '<a href="#" onclick="showSubDialog(\'collectiondelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
|
||||
}
|
||||
|
||||
var newRow = [
|
||||
result[i].name,
|
||||
result[i].description,
|
||||
statusText,
|
||||
packageSize,
|
||||
'<div style="text-align: right;">' + downloadLink + editButton + deleteButton + '</div>'
|
||||
];
|
||||
|
||||
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
||||
}
|
||||
|
||||
targetDiv.appendChild(newTable);
|
||||
}
|
||||
});
|
||||
}
|
||||
// set up the page
|
||||
SetupPage();
|
||||
</script>
|
85
gaseous-server/wwwroot/pages/collections.js
Normal file
@@ -0,0 +1,85 @@
|
||||
function SetupPage() {
|
||||
backgroundImageHandler = new BackgroundImageRotator(['/images/CollectionsWallpaper.jpg']);
|
||||
|
||||
var newCollectionButton = document.getElementById('collection_new');
|
||||
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
|
||||
newCollectionButton.style.display = '';
|
||||
} else {
|
||||
newCollectionButton.style.display = 'none';
|
||||
}
|
||||
|
||||
GetCollections();
|
||||
}
|
||||
|
||||
function GetCollections() {
|
||||
ajaxCall('/api/v1.1/Collections', 'GET', function (result) {
|
||||
if (result) {
|
||||
var targetDiv = document.getElementById('collection_table_location');
|
||||
targetDiv.innerHTML = '';
|
||||
|
||||
var newTable = document.createElement('table');
|
||||
newTable.id = 'romtable';
|
||||
newTable.className = 'romtable';
|
||||
newTable.setAttribute('cellspacing', 0);
|
||||
newTable.appendChild(createTableRow(true, ['Name', 'Description', 'Download Status', 'Size', '']));
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
var statusText = result[i].buildStatus;
|
||||
var downloadLink = '';
|
||||
var packageSize = '-';
|
||||
var inProgress = false;
|
||||
switch (result[i].buildStatus) {
|
||||
case 'NoStatus':
|
||||
statusText = '-';
|
||||
break;
|
||||
case "WaitingForBuild":
|
||||
statusText = 'Build pending';
|
||||
inProgress = true;
|
||||
break;
|
||||
case "Building":
|
||||
statusText = 'Building';
|
||||
inProgress = true;
|
||||
break;
|
||||
case "Completed":
|
||||
statusText = 'Available';
|
||||
downloadLink = '<a href="/api/v1.1/Collections/' + result[i].id + '/Roms/Zip" class="romlink"><img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" /></a>';
|
||||
packageSize = formatBytes(result[i].collectionBuiltSizeBytes);
|
||||
break;
|
||||
case "Failed":
|
||||
statusText = 'Build error';
|
||||
break;
|
||||
default:
|
||||
statusText = result[i].buildStatus;
|
||||
break;
|
||||
}
|
||||
|
||||
if (inProgress == true) {
|
||||
setTimeout(GetCollections, 10000);
|
||||
}
|
||||
|
||||
var editButton = '';
|
||||
var deleteButton = '';
|
||||
|
||||
if (userProfile.roles.includes("Admin") || userProfile.roles.includes("Gamer")) {
|
||||
editButton = '<a href="#" onclick="showDialog(\'collectionedit\', ' + result[i].id + ');" class="romlink"><img src="/images/edit.svg" class="banner_button_image" alt="Edit" title="Edit" /></a>';
|
||||
|
||||
deleteButton = '<a href="#" onclick="showSubDialog(\'collectiondelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
|
||||
}
|
||||
|
||||
var newRow = [
|
||||
result[i].name,
|
||||
result[i].description,
|
||||
statusText,
|
||||
packageSize,
|
||||
'<div style="text-align: right;">' + downloadLink + editButton + deleteButton + '</div>'
|
||||
];
|
||||
|
||||
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
||||
}
|
||||
|
||||
targetDiv.appendChild(newTable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SetupPage();
|
@@ -10,7 +10,8 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="collection_filter_box" style="position: absolute; top: 40px; left: 0px; bottom: 5px; width: 40%; max-width: 40%; overflow-x: scroll;">
|
||||
<div id="collection_filter_box"
|
||||
style="position: absolute; top: 40px; left: 0px; bottom: 5px; width: 40%; max-width: 40%; overflow-x: scroll;">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="width: 25%;">Platforms</th>
|
||||
@@ -26,7 +27,8 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Player Perspectives</th>
|
||||
<td><select id="collection_playerperspectives" style="width: 100%;" multiple="multiple"></select></td>
|
||||
<td><select id="collection_playerperspectives" style="width: 100%;" multiple="multiple"></select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Themes</th>
|
||||
@@ -50,17 +52,24 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Maximum size per platform (bytes)</th>
|
||||
<td><input id="collection_maxplatformsize" type="number" placeholder="0" step="1048576" oninput="DisplayFormattedBytes('collection_maxplatformsize', 'maxplatformsize_label');"><span id="maxplatformsize_label" style="margin-left: 10px;"></span></td>
|
||||
<td><input id="collection_maxplatformsize" type="number" placeholder="0" step="1048576"
|
||||
oninput="DisplayFormattedBytes('collection_maxplatformsize', 'maxplatformsize_label');"><span
|
||||
id="maxplatformsize_label" style="margin-left: 10px;"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Maximum collection size (bytes)</th>
|
||||
<td><input id="collection_maxcollectionsize" type="number" placeholder="0" step="1048576" oninput="DisplayFormattedBytes('collection_maxcollectionsize', 'maxcollectionsize_label');"><span id="maxcollectionsize_label" style="margin-left: 10px;"></span></td></td>
|
||||
<td><input id="collection_maxcollectionsize" type="number" placeholder="0" step="1048576"
|
||||
oninput="DisplayFormattedBytes('collection_maxcollectionsize', 'maxcollectionsize_label');"><span
|
||||
id="maxcollectionsize_label" style="margin-left: 10px;"></span></td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Directory Layout</th>
|
||||
<td>
|
||||
<select id="collection_directorylayout" style="width: 100%;" data-minimum-results-for-search="Infinity" onchange="DisplayDirectoryLabel();">
|
||||
<option id="collection_directorylayout_gaseous" selected="selected" value="Gaseous">Standard</option>
|
||||
<select id="collection_directorylayout" style="width: 100%;"
|
||||
data-minimum-results-for-search="Infinity" onchange="DisplayDirectoryLabel();">
|
||||
<option id="collection_directorylayout_gaseous" selected="selected" value="Gaseous">Standard
|
||||
</option>
|
||||
<option id="collection_directorylayout_retropie" value="RetroPie">RetroPie</option>
|
||||
</select>
|
||||
</td>
|
||||
@@ -74,7 +83,8 @@
|
||||
<p>Standard layout: /<IGDB Platform Slug>/<IGDB Game Slug>/Game ROMs</p>
|
||||
<p>Example: /genesis-slash-megadrive/sonic-the-hedgehog/Sonic the Hedgehog.smd</p>
|
||||
</span>
|
||||
<span id="collection_directorylayout_retropie_label" style="display: none;" name="collection_directorylayout_label">
|
||||
<span id="collection_directorylayout_retropie_label" style="display: none;"
|
||||
name="collection_directorylayout_label">
|
||||
<p>RetroPie layout: /roms/<RetroPie Platform Label>/Game ROMs</p>
|
||||
<p>Example: /roms/megadrive/Sonic the Hedgehog.smd</p>
|
||||
</span>
|
||||
@@ -85,7 +95,8 @@
|
||||
Include BIOS files (if available)
|
||||
</th>
|
||||
<td>
|
||||
<select id="collection_includebios" style="width: 100%;" data-minimum-results-for-search="Infinity">
|
||||
<select id="collection_includebios" style="width: 100%;"
|
||||
data-minimum-results-for-search="Infinity">
|
||||
<option id="collection_includebios_yes" selected="selected" value="true">Yes</option>
|
||||
<option id="collection_includebios_no" value="false">No</option>
|
||||
</select>
|
||||
@@ -118,15 +129,18 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="collectionedit_previewbox" style="position: absolute; top: 40px; right: 0px; bottom: 60px; width: 60%; overflow-x: scroll;">
|
||||
<div id="collectionedit_previewbox"
|
||||
style="position: absolute; top: 40px; right: 0px; bottom: 60px; width: 60%; overflow-x: scroll;">
|
||||
<div id="collectionedit_previewbox_content" style="margin: 5px;">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="collectionedit_previewbox_size" style="position: absolute; right: 10px; bottom: 30px; width: 60%; height: 20px; text-align: right;">
|
||||
<div id="collectionedit_previewbox_size"
|
||||
style="position: absolute; right: 10px; bottom: 30px; width: 60%; height: 20px; text-align: right;">
|
||||
|
||||
</div>
|
||||
<div id="collectionedit_previewbox_controls" style="position: absolute; right: 10px; bottom: 0px; width: 60%; height: 25px; text-align: right;">
|
||||
<div id="collectionedit_previewbox_controls"
|
||||
style="position: absolute; right: 10px; bottom: 0px; width: 60%; height: 25px; text-align: right;">
|
||||
<button id="collectionedit_preview" onclick="GetPreview();">Preview</button>
|
||||
<button id="collectionedit_cancel" onclick="closeDialog();">Cancel</button>
|
||||
<button id="collectionedit_ok" onclick="SaveCollection();">Ok</button>
|
||||
@@ -272,7 +286,7 @@
|
||||
ajaxCall(
|
||||
'/api/v1.1/Collections/' + modalVariables,
|
||||
'GET',
|
||||
function(result) {
|
||||
function (result) {
|
||||
if (result.name) { document.getElementById('collection_name').value = result.name; }
|
||||
if (result.description) { document.getElementById('collection_description').value = result.description; }
|
||||
if (result.minimumRating != -1) { document.getElementById('collection_userrating_min').value = result.minimumRating; }
|
||||
@@ -358,10 +372,10 @@
|
||||
ajaxCall(
|
||||
'/api/v1.1/Collections/' + modalVariables,
|
||||
'PATCH',
|
||||
function(result) {
|
||||
function (result) {
|
||||
location.reload();
|
||||
},
|
||||
function(error) {
|
||||
function (error) {
|
||||
alert(error);
|
||||
},
|
||||
JSON.stringify(item)
|
||||
@@ -371,10 +385,10 @@
|
||||
ajaxCall(
|
||||
'/api/v1.1/Collections',
|
||||
'POST',
|
||||
function(result) {
|
||||
function (result) {
|
||||
location.reload();
|
||||
},
|
||||
function(error) {
|
||||
function (error) {
|
||||
alert(error);
|
||||
},
|
||||
JSON.stringify(item)
|
||||
@@ -440,10 +454,10 @@
|
||||
ajaxCall(
|
||||
'/api/v1.1/Collections/Preview',
|
||||
'POST',
|
||||
function(result) {
|
||||
function (result) {
|
||||
DisplayPreview(result, 'collectionedit_previewbox_content');
|
||||
},
|
||||
function(error) {
|
||||
function (error) {
|
||||
console.log(JSON.stringify(error));
|
||||
},
|
||||
JSON.stringify(item)
|
||||
@@ -592,7 +606,7 @@
|
||||
gameImage.className = 'game_tile_image game_tile_image_small lazy';
|
||||
gameImage.src = '/images/unknowngame.png';
|
||||
if (gameItem.cover) {
|
||||
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameItem.id + '/cover/image/cover_small/' + gameItem.coverItem.imageId + '.jpg');
|
||||
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameItem.id + '/cover/' + gameItem.cover.id + '/image/cover_small/' + gameItem.coverItem.imageId + '.jpg');
|
||||
} else {
|
||||
gameImage.className = 'game_tile_image game_tile_image_small unknown';
|
||||
}
|
||||
@@ -658,7 +672,7 @@
|
||||
|
||||
var labelToDisplay = '';
|
||||
if (directoryLayoutMode) {
|
||||
switch(directoryLayoutMode) {
|
||||
switch (directoryLayoutMode) {
|
||||
case "Gaseous":
|
||||
// standard mode
|
||||
labelToDisplay = 'collection_directorylayout_gaseous_label';
|
||||
|