Merge branch 'master' into trunk-io

This commit is contained in:
Georges-Antoine Assi
2024-05-23 10:05:35 -04:00
61 changed files with 518 additions and 457 deletions

View File

@@ -21,14 +21,14 @@ lint:
- actionlint@1.7.0
- bandit@1.7.8
- black@24.4.2
- checkov@3.2.98
- checkov@3.2.106
- git-diff-check
- isort@5.13.2
- mypy@1.10.0
- osv-scanner@1.7.3
- oxipng@9.1.1
- prettier@3.2.5
- ruff@0.4.4
- ruff@0.4.5
- shellcheck@0.10.0
- shfmt@3.6.0
- svgo@3.3.2

View File

@@ -6,10 +6,11 @@ from dotenv import load_dotenv
load_dotenv()
# UVICORN
# GUNICORN
DEV_PORT: Final = int(os.environ.get("VITE_BACKEND_DEV_PORT", "5000"))
DEV_HOST: Final = "127.0.0.1"
ROMM_HOST: Final = os.environ.get("ROMM_HOST", DEV_HOST)
GUNICORN_WORKERS: Final = int(os.environ.get("GUNICORN_WORKERS", 2))
# PATHS
ROMM_BASE_PATH: Final = os.environ.get("ROMM_BASE_PATH", "/romm")

View File

@@ -95,9 +95,7 @@ def get_platform(request: Request, id: int) -> PlatformSchema:
PlatformSchema: Platform
"""
return PlatformSchema.from_orm_with_request(
db_platform_handler.get_platforms(id), request
)
return db_platform_handler.get_platforms(id)
@protected_route(router.put, "/platforms/{id}", ["platforms.write"])

View File

@@ -1,7 +1,4 @@
from typing import Optional
from fastapi import Request
from models.platform import Platform
from pydantic import BaseModel, Field
from .firmware import FirmwareSchema
@@ -17,19 +14,7 @@ class PlatformSchema(BaseModel):
name: str
logo_path: Optional[str] = ""
rom_count: int
firmware_files: list[FirmwareSchema] = Field(default_factory=list)
firmware: list[FirmwareSchema] = Field(default_factory=list)
class Config:
from_attributes = True
@classmethod
def from_orm_with_request(
cls, db_platform: Platform, request: Request
) -> "PlatformSchema":
platform = cls.model_validate(db_platform)
platform.firmware_files = [
FirmwareSchema.model_validate(f)
for f in sorted(db_platform.firmware, key=lambda x: x.file_name)
]
return platform

View File

@@ -23,7 +23,7 @@ from handler.filesystem.base_handler import CoverSize
from handler.metadata import meta_igdb_handler, meta_moby_handler
from logger.logger import log
from stream_zip import ZIP_AUTO, stream_zip # type: ignore[import]
from urllib.parse import quote
router = APIRouter()
@@ -236,7 +236,7 @@ def get_rom_content(
return CustomStreamingResponse(
zipped_chunks,
media_type="application/zip",
headers={"Content-Disposition": f'attachment; filename="{file_name}.zip"'},
headers={"Content-Disposition": f'attachment; filename="{quote(file_name)}.zip"'},
emit_body={"id": rom.id},
)

View File

@@ -1,41 +1,58 @@
import functools
from sqlalchemy import delete, or_
from sqlalchemy.orm import Session, Query, joinedload
from decorators.database import begin_session
from models.platform import Platform
from models.rom import Rom
from sqlalchemy import delete, func, or_, select
from sqlalchemy.orm import Session
from .base_handler import DBBaseHandler
def with_query(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
session = kwargs.get("session")
if session is None:
raise ValueError("session is required")
kwargs["query"] = session.query(Platform).options(joinedload(Platform.roms))
return func(*args, **kwargs)
return wrapper
class DBPlatformsHandler(DBBaseHandler):
@begin_session
def add_platform(self, platform: Platform, session: Session = None) -> Platform:
return session.merge(platform)
@with_query
def add_platform(
self, platform: Platform, query: Query = None, session: Session = None
) -> Platform | None:
session.merge(platform)
session.flush()
return query.filter(Platform.fs_slug == platform.fs_slug).first()
@begin_session
@with_query
def get_platforms(
self, id: int | None = None, session: Session = None
) -> Platform | list[Platform] | None:
self, id: int = None, query: Query = None, session: Session = None
) -> list[Platform] | Platform | None:
return (
session.get(Platform, id)
query.get(id)
if id
else (
session.scalars(select(Platform).order_by(Platform.name.asc())) # type: ignore[attr-defined]
.unique()
.all()
)
else (session.scalars(query.order_by(Platform.name.asc())).unique().all())
)
@begin_session
@with_query
def get_platform_by_fs_slug(
self, fs_slug: str, session: Session = None
self, fs_slug: str, query: Query = None, session: Session = None
) -> Platform | None:
return session.scalars(
select(Platform).filter_by(fs_slug=fs_slug).limit(1)
).first()
return session.scalars(query.filter_by(fs_slug=fs_slug).limit(1)).first()
@begin_session
def delete_platform(self, id: int, session: Session = None) -> None:
def delete_platform(self, id: int, session: Session = None) -> int:
# Remove all roms from that platforms first
session.execute(
delete(Rom)
@@ -49,13 +66,7 @@ class DBPlatformsHandler(DBBaseHandler):
)
@begin_session
def get_rom_count(self, platform_id: int, session: Session = None) -> int:
return session.scalar(
select(func.count()).select_from(Rom).filter_by(platform_id=platform_id)
)
@begin_session
def purge_platforms(self, fs_platforms: list[str], session: Session = None) -> None:
def purge_platforms(self, fs_platforms: list[str], session: Session = None) -> int:
return session.execute(
delete(Platform)
.where(or_(Platform.fs_slug.not_in(fs_platforms), Platform.slug.is_(None))) # type: ignore[attr-defined]

View File

@@ -465,7 +465,8 @@ class IGDBBaseHandler(MetadataHandler):
if not IGDB_API_ENABLED:
return []
return [self.get_rom_by_id(igdb_id)]
rom = self.get_rom_by_id(igdb_id)
return [rom] if rom["igdb_id"] else []
@check_twitch_token
def get_matched_roms_by_name(

View File

@@ -260,14 +260,14 @@ class MobyGamesHandler(MetadataHandler):
def get_rom_by_id(self, moby_id: int) -> MobyGamesRom:
if not MOBY_API_ENABLED:
return MobyGamesRom(moby_id=moby_id)
return MobyGamesRom(moby_id=None)
url = yarl.URL(self.games_url).with_query(id=moby_id)
roms = self._request(str(url)).get("games", [])
res = pydash.get(roms, "[0]", None)
if not res:
return MobyGamesRom(moby_id=moby_id)
return MobyGamesRom(moby_id=None)
rom = {
"moby_id": res["game_id"],
@@ -285,7 +285,8 @@ class MobyGamesHandler(MetadataHandler):
if not MOBY_API_ENABLED:
return []
return [self.get_rom_by_id(moby_id)]
rom = self.get_rom_by_id(moby_id)
return [rom] if rom["moby_id"] else []
def get_matched_roms_by_name(
self, search_term: str, platform_moby_id: int

View File

@@ -1,8 +1,9 @@
from sqlalchemy import Column, Integer, String, select, func
from sqlalchemy.orm import Mapped, relationship, column_property
from models.base import BaseModel
from models.firmware import Firmware
from models.rom import Rom
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import Mapped, relationship
class Platform(BaseModel):
@@ -24,11 +25,10 @@ class Platform(BaseModel):
"Firmware", lazy="selectin", back_populates="platform"
)
@property
def rom_count(self) -> int:
from handler.database import db_platform_handler
return db_platform_handler.get_rom_count(self.id)
# This runs a subquery to get the count of roms for the platform
rom_count = column_property(
select(func.count(Rom.id)).where(Rom.platform_id == id).scalar_subquery()
)
def __repr__(self) -> str:
return self.name

View File

@@ -35,20 +35,20 @@ error_log() {
}
# function that runs or main process and creates a corresponding PID file,
start_bin_gunicorn() {
# cleanup potentially leftover socket
rm /tmp/gunicorn.sock -f
# Commands to start our main application and store its PID to check for crashes
info_log "starting gunicorn"
gunicorn \
--access-logfile - \
--error-logfile - \
--worker-class uvicorn.workers.UvicornWorker \
--bind=0.0.0.0:5000 \
--bind=unix:/tmp/gunicorn.sock \
--pid=/tmp/gunicorn.pid \
--workers 2 \
main:app &
start_bin_gunicorn () {
# cleanup potentially leftover socket
rm /tmp/gunicorn.sock -f
# Commands to start our main application and store its PID to check for crashes
info_log "starting gunicorn"
gunicorn \
--access-logfile - \
--error-logfile - \
--worker-class uvicorn.workers.UvicornWorker \
--bind=0.0.0.0:5000 \
--bind=unix:/tmp/gunicorn.sock \
--pid=/tmp/gunicorn.pid \
--workers ${GUNICORN_WORKERS:=2} \
main:app &
}
# Commands to start nginx (handling PID creation internally)

View File

@@ -1,6 +1,9 @@
ROMM_BASE_PATH=/path/to/romm_mock
VITE_BACKEND_DEV_PORT=5000
# Gunicorn (optional)
ROMM_HOST=localhost
GUNICORN_WORKERS=4 # (2 × CPU cores) + 1
# IGDB credentials
IGDB_CLIENT_ID=

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,17 +1,21 @@
<script setup lang="ts">
import Notification from "@/components/Notification.vue";
import api from "@/services/api/index";
import userApi from "@/services/api/user";
import platformApi from "@/services/api/platform";
import socket from "@/services/socket";
import storeConfig from "@/stores/config";
import storeGalleryFilter from "@/stores/galleryFilter";
import storeHeartbeat from "@/stores/heartbeat";
import storeRoms, { type Rom } from "@/stores/roms";
import storePlatforms from "@/stores/platforms";
import storeAuth from "@/stores/auth";
import storeScanning from "@/stores/scanning";
import type { Events } from "@/types/emitter";
import { normalizeString } from "@/utils";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, onBeforeMount, onBeforeUnmount } from "vue";
import { inject, onMounted, onBeforeUnmount } from "vue";
// Props
const scanningStore = storeScanning();
@@ -24,6 +28,8 @@ const emitter = inject<Emitter<Events>>("emitter");
// Props
const heartbeat = storeHeartbeat();
const configStore = storeConfig();
const auth = storeAuth();
const platformsStore = storePlatforms();
socket.on(
"scan:scanning_platform",
@@ -93,16 +99,44 @@ onBeforeUnmount(() => {
socket.off("scan:scanning_rom");
socket.off("scan:done");
socket.off("scan:done_ko");
document.removeEventListener("network-quiesced", fetchHomeData);
});
onBeforeMount(() => {
api.get("/heartbeat").then(({ data: data }) => {
heartbeat.set(data);
});
onMounted(() => {
api.get("/config").then(({ data: data }) => {
configStore.set(data);
});
});
function fetchHomeData() {
// Remove it so it's not called multiple times
document.removeEventListener("network-quiesced", fetchHomeData);
api.get("/heartbeat").then(({ data: data }) => {
heartbeat.set(data);
});
platformApi
.getPlatforms()
.then(({ data: platforms }) => {
platformsStore.set(platforms);
})
.catch((error) => {
console.error(error);
});
userApi
.fetchCurrentUser()
.then(({ data: user }) => {
auth.setUser(user);
})
.catch((error) => {
console.error(error);
});
}
document.addEventListener("network-quiesced", fetchHomeData);
</script>
<template>

View File

@@ -3,45 +3,45 @@
/* tslint:disable */
/* eslint-disable */
export type { AddFirmwareResponse } from "./models/AddFirmwareResponse";
export type { AddRomsResponse } from "./models/AddRomsResponse";
export type { Body_add_firmware_firmware_post } from "./models/Body_add_firmware_firmware_post";
export type { Body_add_roms_roms_post } from "./models/Body_add_roms_roms_post";
export type { Body_add_saves_saves_post } from "./models/Body_add_saves_saves_post";
export type { Body_add_screenshots_screenshots_post } from "./models/Body_add_screenshots_screenshots_post";
export type { Body_add_states_states_post } from "./models/Body_add_states_states_post";
export type { Body_token_token_post } from "./models/Body_token_token_post";
export type { Body_update_rom_roms__id__put } from "./models/Body_update_rom_roms__id__put";
export type { Body_update_user_users__id__put } from "./models/Body_update_user_users__id__put";
export type { ConfigResponse } from "./models/ConfigResponse";
export type { CursorPage_RomSchema_ } from "./models/CursorPage_RomSchema_";
export type { FirmwareSchema } from "./models/FirmwareSchema";
export type { HeartbeatResponse } from "./models/HeartbeatResponse";
export type { HTTPValidationError } from "./models/HTTPValidationError";
export type { IGDBPlatform } from "./models/IGDBPlatform";
export type { IGDBRelatedGame } from "./models/IGDBRelatedGame";
export type { MessageResponse } from "./models/MessageResponse";
export type { MetadataSourcesDict } from "./models/MetadataSourcesDict";
export type { MobyGamesPlatform } from "./models/MobyGamesPlatform";
export type { PlatformSchema } from "./models/PlatformSchema";
export type { Role } from "./models/Role";
export type { RomIGDBMetadata } from "./models/RomIGDBMetadata";
export type { RomMobyMetadata } from "./models/RomMobyMetadata";
export type { RomNoteSchema } from "./models/RomNoteSchema";
export type { RomSchema } from "./models/RomSchema";
export type { SaveSchema } from "./models/SaveSchema";
export type { SchedulerDict } from "./models/SchedulerDict";
export type { ScreenshotSchema } from "./models/ScreenshotSchema";
export type { SearchRomSchema } from "./models/SearchRomSchema";
export type { StateSchema } from "./models/StateSchema";
export type { StatsReturn } from "./models/StatsReturn";
export type { TaskDict } from "./models/TaskDict";
export type { TinfoilFeedSchema } from "./models/TinfoilFeedSchema";
export type { TokenResponse } from "./models/TokenResponse";
export type { UploadedSavesResponse } from "./models/UploadedSavesResponse";
export type { UploadedScreenshotsResponse } from "./models/UploadedScreenshotsResponse";
export type { UploadedStatesResponse } from "./models/UploadedStatesResponse";
export type { UserSchema } from "./models/UserSchema";
export type { ValidationError } from "./models/ValidationError";
export type { WatcherDict } from "./models/WatcherDict";
export type { WebrcadeFeedSchema } from "./models/WebrcadeFeedSchema";
export type { AddFirmwareResponse } from './models/AddFirmwareResponse';
export type { AddRomsResponse } from './models/AddRomsResponse';
export type { Body_add_firmware_firmware_post } from './models/Body_add_firmware_firmware_post';
export type { Body_add_roms_roms_post } from './models/Body_add_roms_roms_post';
export type { Body_add_saves_saves_post } from './models/Body_add_saves_saves_post';
export type { Body_add_screenshots_screenshots_post } from './models/Body_add_screenshots_screenshots_post';
export type { Body_add_states_states_post } from './models/Body_add_states_states_post';
export type { Body_token_token_post } from './models/Body_token_token_post';
export type { Body_update_rom_roms__id__put } from './models/Body_update_rom_roms__id__put';
export type { Body_update_user_users__id__put } from './models/Body_update_user_users__id__put';
export type { ConfigResponse } from './models/ConfigResponse';
export type { CursorPage_RomSchema_ } from './models/CursorPage_RomSchema_';
export type { FirmwareSchema } from './models/FirmwareSchema';
export type { HeartbeatResponse } from './models/HeartbeatResponse';
export type { HTTPValidationError } from './models/HTTPValidationError';
export type { IGDBMetadataPlatform } from './models/IGDBMetadataPlatform';
export type { IGDBRelatedGame } from './models/IGDBRelatedGame';
export type { MessageResponse } from './models/MessageResponse';
export type { MetadataSourcesDict } from './models/MetadataSourcesDict';
export type { MobyMetadataPlatform } from './models/MobyMetadataPlatform';
export type { PlatformSchema } from './models/PlatformSchema';
export type { Role } from './models/Role';
export type { RomIGDBMetadata } from './models/RomIGDBMetadata';
export type { RomMobyMetadata } from './models/RomMobyMetadata';
export type { RomNoteSchema } from './models/RomNoteSchema';
export type { RomSchema } from './models/RomSchema';
export type { SaveSchema } from './models/SaveSchema';
export type { SchedulerDict } from './models/SchedulerDict';
export type { ScreenshotSchema } from './models/ScreenshotSchema';
export type { SearchRomSchema } from './models/SearchRomSchema';
export type { StateSchema } from './models/StateSchema';
export type { StatsReturn } from './models/StatsReturn';
export type { TaskDict } from './models/TaskDict';
export type { TinfoilFeedSchema } from './models/TinfoilFeedSchema';
export type { TokenResponse } from './models/TokenResponse';
export type { UploadedSavesResponse } from './models/UploadedSavesResponse';
export type { UploadedScreenshotsResponse } from './models/UploadedScreenshotsResponse';
export type { UploadedStatesResponse } from './models/UploadedStatesResponse';
export type { UserSchema } from './models/UserSchema';
export type { ValidationError } from './models/ValidationError';
export type { WatcherDict } from './models/WatcherDict';
export type { WebrcadeFeedSchema } from './models/WebrcadeFeedSchema';

View File

@@ -3,9 +3,10 @@
/* tslint:disable */
/* eslint-disable */
import type { FirmwareSchema } from "./FirmwareSchema";
import type { FirmwareSchema } from './FirmwareSchema';
export type AddFirmwareResponse = {
uploaded: number;
firmware: Array<FirmwareSchema>;
uploaded: number;
firmware: Array<FirmwareSchema>;
};

View File

@@ -4,6 +4,7 @@
/* eslint-disable */
export type AddRomsResponse = {
uploaded_roms: Array<string>;
skipped_roms: Array<string>;
uploaded_roms: Array<string>;
skipped_roms: Array<string>;
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_add_firmware_firmware_post = {
files: Array<Blob>;
files?: (Array<Blob> | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_add_roms_roms_post = {
roms: Array<Blob>;
roms?: (Array<Blob> | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_add_saves_saves_post = {
saves: Array<Blob>;
saves?: (Array<Blob> | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_add_screenshots_screenshots_post = {
screenshots: Array<Blob>;
screenshots?: (Array<Blob> | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_add_states_states_post = {
states: Array<Blob>;
states?: (Array<Blob> | null);
};

View File

@@ -4,11 +4,12 @@
/* eslint-disable */
export type Body_token_token_post = {
grant_type?: string;
scope?: string;
username?: string | null;
password?: string | null;
client_id?: string | null;
client_secret?: string | null;
refresh_token?: string | null;
grant_type?: string;
scope?: string;
username?: (string | null);
password?: (string | null);
client_id?: (string | null);
client_secret?: (string | null);
refresh_token?: (string | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_update_rom_roms__id__put = {
artwork?: Blob | null;
artwork?: (Blob | null);
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type Body_update_user_users__id__put = {
avatar?: Blob | null;
avatar?: (Blob | null);
};

View File

@@ -4,15 +4,16 @@
/* eslint-disable */
export type ConfigResponse = {
EXCLUDED_PLATFORMS: Array<string>;
EXCLUDED_SINGLE_EXT: Array<string>;
EXCLUDED_SINGLE_FILES: Array<string>;
EXCLUDED_MULTI_FILES: Array<string>;
EXCLUDED_MULTI_PARTS_EXT: Array<string>;
EXCLUDED_MULTI_PARTS_FILES: Array<string>;
PLATFORMS_BINDING: Record<string, string>;
PLATFORMS_VERSIONS: Record<string, string>;
ROMS_FOLDER_NAME: string;
FIRMWARE_FOLDER_NAME: string;
HIGH_PRIO_STRUCTURE_PATH: string;
EXCLUDED_PLATFORMS: Array<string>;
EXCLUDED_SINGLE_EXT: Array<string>;
EXCLUDED_SINGLE_FILES: Array<string>;
EXCLUDED_MULTI_FILES: Array<string>;
EXCLUDED_MULTI_PARTS_EXT: Array<string>;
EXCLUDED_MULTI_PARTS_FILES: Array<string>;
PLATFORMS_BINDING: Record<string, string>;
PLATFORMS_VERSIONS: Record<string, string>;
ROMS_FOLDER_NAME: string;
FIRMWARE_FOLDER_NAME: string;
HIGH_PRIO_STRUCTURE_PATH: string;
};

View File

@@ -3,28 +3,29 @@
/* tslint:disable */
/* eslint-disable */
import type { RomSchema } from "./RomSchema";
import type { RomSchema } from './RomSchema';
export type CursorPage_RomSchema_ = {
items: Array<RomSchema>;
/**
* Total items
*/
total?: number | null;
/**
* Cursor to refetch the current page
*/
current_page?: string | null;
/**
* Cursor to refetch the current page starting from the last item
*/
current_page_backwards?: string | null;
/**
* Cursor for the previous page
*/
previous_page?: string | null;
/**
* Cursor for the next page
*/
next_page?: string | null;
items: Array<RomSchema>;
/**
* Total items
*/
total?: (number | null);
/**
* Cursor to refetch the current page
*/
current_page?: (string | null);
/**
* Cursor to refetch the current page starting from the last item
*/
current_page_backwards?: (string | null);
/**
* Cursor for the previous page
*/
previous_page?: (string | null);
/**
* Cursor for the next page
*/
next_page?: (string | null);
};

View File

@@ -4,16 +4,17 @@
/* eslint-disable */
export type FirmwareSchema = {
id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
is_verified: boolean;
crc_hash: string;
md5_hash: string;
sha1_hash: string;
id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
is_verified: boolean;
crc_hash: string;
md5_hash: string;
sha1_hash: string;
};

View File

@@ -3,8 +3,9 @@
/* tslint:disable */
/* eslint-disable */
import type { ValidationError } from "./ValidationError";
import type { ValidationError } from './ValidationError';
export type HTTPValidationError = {
detail?: Array<ValidationError>;
detail?: Array<ValidationError>;
};

View File

@@ -3,15 +3,16 @@
/* tslint:disable */
/* eslint-disable */
import type { MetadataSourcesDict } from "./MetadataSourcesDict";
import type { SchedulerDict } from "./SchedulerDict";
import type { WatcherDict } from "./WatcherDict";
import type { MetadataSourcesDict } from './MetadataSourcesDict';
import type { SchedulerDict } from './SchedulerDict';
import type { WatcherDict } from './WatcherDict';
export type HeartbeatResponse = {
VERSION: string;
NEW_VERSION: string;
WATCHER: WatcherDict;
SCHEDULER: SchedulerDict;
ANY_SOURCE_ENABLED: boolean;
METADATA_SOURCES: MetadataSourcesDict;
VERSION: string;
NEW_VERSION: string;
WATCHER: WatcherDict;
SCHEDULER: SchedulerDict;
ANY_SOURCE_ENABLED: boolean;
METADATA_SOURCES: MetadataSourcesDict;
};

View File

@@ -3,7 +3,8 @@
/* tslint:disable */
/* eslint-disable */
export type IGDBPlatform = {
igdb_id: number;
name?: string;
export type IGDBMetadataPlatform = {
igdb_id: number;
name: string;
};

View File

@@ -4,9 +4,10 @@
/* eslint-disable */
export type IGDBRelatedGame = {
id: number;
name: string;
slug: string;
type: string;
cover_url: string;
id: number;
name: string;
slug: string;
type: string;
cover_url: string;
};

View File

@@ -4,5 +4,6 @@
/* eslint-disable */
export type MessageResponse = {
msg: string;
msg: string;
};

View File

@@ -4,6 +4,7 @@
/* eslint-disable */
export type MetadataSourcesDict = {
IGDB_API_ENABLED: boolean;
MOBY_API_ENABLED: boolean;
IGDB_API_ENABLED: boolean;
MOBY_API_ENABLED: boolean;
};

View File

@@ -3,7 +3,8 @@
/* tslint:disable */
/* eslint-disable */
export type MobyGamesPlatform = {
moby_id: number;
name?: string;
export type MobyMetadataPlatform = {
moby_id: number;
name: string;
};

View File

@@ -3,17 +3,18 @@
/* tslint:disable */
/* eslint-disable */
import type { FirmwareSchema } from "./FirmwareSchema";
import type { FirmwareSchema } from './FirmwareSchema';
export type PlatformSchema = {
id: number;
slug: string;
fs_slug: string;
igdb_id?: number | null;
sgdb_id?: number | null;
moby_id?: number | null;
name: string;
logo_path?: string | null;
rom_count: number;
firmware_files?: Array<FirmwareSchema>;
id: number;
slug: string;
fs_slug: string;
igdb_id?: (number | null);
sgdb_id?: (number | null);
moby_id?: (number | null);
name: string;
logo_path?: (string | null);
rom_count: number;
firmware?: Array<FirmwareSchema>;
};

View File

@@ -3,4 +3,4 @@
/* tslint:disable */
/* eslint-disable */
export type Role = "viewer" | "editor" | "admin";
export type Role = 'viewer' | 'editor' | 'admin';

View File

@@ -3,25 +3,26 @@
/* tslint:disable */
/* eslint-disable */
import type { IGDBPlatform } from "./IGDBPlatform";
import type { IGDBRelatedGame } from "./IGDBRelatedGame";
import type { IGDBMetadataPlatform } from './IGDBMetadataPlatform';
import type { IGDBRelatedGame } from './IGDBRelatedGame';
export type RomIGDBMetadata = {
total_rating?: string;
aggregated_rating?: string;
first_release_date?: number | null;
genres?: Array<string>;
franchises?: Array<string>;
alternative_names?: Array<string>;
collections?: Array<string>;
companies?: Array<string>;
game_modes?: Array<string>;
platforms?: Array<IGDBPlatform>;
expansions?: Array<IGDBRelatedGame>;
dlcs?: Array<IGDBRelatedGame>;
remasters?: Array<IGDBRelatedGame>;
remakes?: Array<IGDBRelatedGame>;
expanded_games?: Array<IGDBRelatedGame>;
ports?: Array<IGDBRelatedGame>;
similar_games?: Array<IGDBRelatedGame>;
total_rating?: string;
aggregated_rating?: string;
first_release_date?: (number | null);
genres?: Array<string>;
franchises?: Array<string>;
alternative_names?: Array<string>;
collections?: Array<string>;
companies?: Array<string>;
game_modes?: Array<string>;
platforms?: Array<IGDBMetadataPlatform>;
expansions?: Array<IGDBRelatedGame>;
dlcs?: Array<IGDBRelatedGame>;
remasters?: Array<IGDBRelatedGame>;
remakes?: Array<IGDBRelatedGame>;
expanded_games?: Array<IGDBRelatedGame>;
ports?: Array<IGDBRelatedGame>;
similar_games?: Array<IGDBRelatedGame>;
};

View File

@@ -3,11 +3,12 @@
/* tslint:disable */
/* eslint-disable */
import type { MobyGamesPlatform } from "./MobyGamesPlatform";
import type { MobyMetadataPlatform } from './MobyMetadataPlatform';
export type RomMobyMetadata = {
moby_score?: string;
genres?: Array<string>;
alternate_titles?: Array<string>;
platforms?: Array<MobyGamesPlatform>;
moby_score?: string;
genres?: Array<string>;
alternate_titles?: Array<string>;
platforms?: Array<MobyMetadataPlatform>;
};

View File

@@ -4,11 +4,11 @@
/* eslint-disable */
export type RomNoteSchema = {
id: number;
user_id: number;
rom_id: number;
last_edited_at: string;
raw_markdown: string;
is_public: boolean;
readonly user__username: string;
id: number;
user_id: number;
rom_id: number;
last_edited_at: string;
raw_markdown: string;
is_public: boolean;
};

View File

@@ -3,56 +3,56 @@
/* tslint:disable */
/* eslint-disable */
import type { RomIGDBMetadata } from "./RomIGDBMetadata";
import type { RomMobyMetadata } from "./RomMobyMetadata";
import type { RomNoteSchema } from "./RomNoteSchema";
import type { SaveSchema } from "./SaveSchema";
import type { ScreenshotSchema } from "./ScreenshotSchema";
import type { StateSchema } from "./StateSchema";
import type { RomIGDBMetadata } from './RomIGDBMetadata';
import type { RomMobyMetadata } from './RomMobyMetadata';
import type { RomNoteSchema } from './RomNoteSchema';
import type { SaveSchema } from './SaveSchema';
import type { ScreenshotSchema } from './ScreenshotSchema';
import type { StateSchema } from './StateSchema';
export type RomSchema = {
id: number;
igdb_id: number | null;
sgdb_id: number | null;
moby_id: number | null;
platform_id: number;
platform_slug: string;
platform_name: string;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
name: string | null;
slug: string | null;
summary: string | null;
first_release_date: number | null;
alternative_names: Array<string>;
genres: Array<string>;
franchises: Array<string>;
collections: Array<string>;
companies: Array<string>;
game_modes: Array<string>;
igdb_metadata: RomIGDBMetadata | null;
moby_metadata: RomMobyMetadata | null;
path_cover_s: string | null;
path_cover_l: string | null;
has_cover: boolean;
url_cover: string | null;
revision: string | null;
regions: Array<string>;
languages: Array<string>;
tags: Array<string>;
multi: boolean;
files: Array<string>;
url_screenshots: Array<string>;
merged_screenshots: Array<string>;
full_path: string;
sibling_roms?: Array<RomSchema>;
user_saves?: Array<SaveSchema>;
user_states?: Array<StateSchema>;
user_screenshots?: Array<ScreenshotSchema>;
user_notes?: Array<RomNoteSchema>;
readonly sort_comparator: string;
id: number;
igdb_id: (number | null);
sgdb_id: (number | null);
moby_id: (number | null);
platform_id: number;
platform_slug: string;
platform_name: string;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
name: (string | null);
slug: (string | null);
summary: (string | null);
first_release_date: (number | null);
alternative_names: Array<string>;
genres: Array<string>;
franchises: Array<string>;
collections: Array<string>;
companies: Array<string>;
game_modes: Array<string>;
igdb_metadata: (RomIGDBMetadata | null);
moby_metadata: (RomMobyMetadata | null);
path_cover_s: (string | null);
path_cover_l: (string | null);
has_cover: boolean;
url_cover: (string | null);
revision: (string | null);
regions: Array<string>;
languages: Array<string>;
tags: Array<string>;
multi: boolean;
files: Array<string>;
url_screenshots: Array<string>;
merged_screenshots: Array<string>;
full_path: string;
sibling_roms?: Array<RomSchema>;
user_saves?: Array<SaveSchema>;
user_states?: Array<StateSchema>;
user_screenshots?: Array<ScreenshotSchema>;
user_notes?: Array<RomNoteSchema>;
};

View File

@@ -3,22 +3,23 @@
/* tslint:disable */
/* eslint-disable */
import type { ScreenshotSchema } from "./ScreenshotSchema";
import type { ScreenshotSchema } from './ScreenshotSchema';
export type SaveSchema = {
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
emulator: string | null;
screenshot: ScreenshotSchema | null;
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
emulator: (string | null);
screenshot: (ScreenshotSchema | null);
};

View File

@@ -3,9 +3,10 @@
/* tslint:disable */
/* eslint-disable */
import type { TaskDict } from "./TaskDict";
import type { TaskDict } from './TaskDict';
export type SchedulerDict = {
RESCAN: TaskDict;
SWITCH_TITLEDB: TaskDict;
RESCAN: TaskDict;
SWITCH_TITLEDB: TaskDict;
};

View File

@@ -4,17 +4,18 @@
/* eslint-disable */
export type ScreenshotSchema = {
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
};

View File

@@ -4,12 +4,13 @@
/* eslint-disable */
export type SearchRomSchema = {
igdb_id?: number | null;
moby_id?: number | null;
slug: string;
name: string;
summary: string;
igdb_url_cover?: string;
moby_url_cover?: string;
url_screenshots: Array<string>;
igdb_id?: (number | null);
moby_id?: (number | null);
slug: string;
name: string;
summary: string;
igdb_url_cover?: string;
moby_url_cover?: string;
url_screenshots: Array<string>;
};

View File

@@ -3,22 +3,23 @@
/* tslint:disable */
/* eslint-disable */
import type { ScreenshotSchema } from "./ScreenshotSchema";
import type { ScreenshotSchema } from './ScreenshotSchema';
export type StateSchema = {
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
emulator: string | null;
screenshot: ScreenshotSchema | null;
id: number;
rom_id: number;
user_id: number;
file_name: string;
file_name_no_tags: string;
file_name_no_ext: string;
file_extension: string;
file_path: string;
file_size_bytes: number;
full_path: string;
download_path: string;
created_at: string;
updated_at: string;
emulator: (string | null);
screenshot: (ScreenshotSchema | null);
};

View File

@@ -4,10 +4,11 @@
/* eslint-disable */
export type StatsReturn = {
PLATFORMS: number;
ROMS: number;
SAVES: number;
STATES: number;
SCREENSHOTS: number;
FILESIZE: number;
PLATFORMS: number;
ROMS: number;
SAVES: number;
STATES: number;
SCREENSHOTS: number;
FILESIZE: number;
};

View File

@@ -4,8 +4,9 @@
/* eslint-disable */
export type TaskDict = {
ENABLED: boolean;
TITLE: string;
MESSAGE: string;
CRON: string;
ENABLED: boolean;
TITLE: string;
MESSAGE: string;
CRON: string;
};

View File

@@ -4,7 +4,8 @@
/* eslint-disable */
export type TinfoilFeedSchema = {
files: Array<Record<string, any>>;
directories: Array<string>;
success: string;
files: Array<Record<string, any>>;
directories: Array<string>;
success: string;
};

View File

@@ -4,8 +4,9 @@
/* eslint-disable */
export type TokenResponse = {
access_token: string;
refresh_token?: string;
token_type: string;
expires: number;
access_token: string;
refresh_token?: string;
token_type: string;
expires: number;
};

View File

@@ -3,9 +3,10 @@
/* tslint:disable */
/* eslint-disable */
import type { SaveSchema } from "./SaveSchema";
import type { SaveSchema } from './SaveSchema';
export type UploadedSavesResponse = {
uploaded: number;
saves: Array<SaveSchema>;
uploaded: number;
saves: Array<SaveSchema>;
};

View File

@@ -3,11 +3,12 @@
/* tslint:disable */
/* eslint-disable */
import type { ScreenshotSchema } from "./ScreenshotSchema";
import type { ScreenshotSchema } from './ScreenshotSchema';
export type UploadedScreenshotsResponse = {
uploaded: number;
screenshots: Array<ScreenshotSchema>;
url_screenshots: Array<string>;
merged_screenshots: Array<string>;
uploaded: number;
screenshots: Array<ScreenshotSchema>;
url_screenshots: Array<string>;
merged_screenshots: Array<string>;
};

View File

@@ -3,9 +3,10 @@
/* tslint:disable */
/* eslint-disable */
import type { StateSchema } from "./StateSchema";
import type { StateSchema } from './StateSchema';
export type UploadedStatesResponse = {
uploaded: number;
states: Array<StateSchema>;
uploaded: number;
states: Array<StateSchema>;
};

View File

@@ -3,15 +3,16 @@
/* tslint:disable */
/* eslint-disable */
import type { Role } from "./Role";
import type { Role } from './Role';
export type UserSchema = {
id: number;
username: string;
enabled: boolean;
role: Role;
oauth_scopes: Array<string>;
avatar_path: string;
last_login: string | null;
last_active: string | null;
id: number;
username: string;
enabled: boolean;
role: Role;
oauth_scopes: Array<string>;
avatar_path: string;
last_login: (string | null);
last_active: (string | null);
};

View File

@@ -4,7 +4,8 @@
/* eslint-disable */
export type ValidationError = {
loc: Array<string | number>;
msg: string;
type: string;
loc: Array<(string | number)>;
msg: string;
type: string;
};

View File

@@ -4,7 +4,8 @@
/* eslint-disable */
export type WatcherDict = {
ENABLED: boolean;
TITLE: string;
MESSAGE: string;
ENABLED: boolean;
TITLE: string;
MESSAGE: string;
};

View File

@@ -4,10 +4,11 @@
/* eslint-disable */
export type WebrcadeFeedSchema = {
title: string;
longTitle: string;
description: string;
thumbnail: string;
background: string;
categories: Array<Record<string, any>>;
title: string;
longTitle: string;
description: string;
thumbnail: string;
background: string;
categories: Array<Record<string, any>>;
};

View File

@@ -44,7 +44,7 @@ function onFullScreenChange() {
label="BIOS"
v-model="biosRef"
:items="
props.platform.firmware_files?.map((f) => ({
props.platform.firmware?.map((f) => ({
title: f.file_name,
value: f,
})) ?? []

View File

@@ -45,7 +45,7 @@ function uploadFirmware() {
.then(({ data }) => {
const { uploaded, firmware } = data;
if (selectedPlatform.value) {
selectedPlatform.value.firmware_files = firmware;
selectedPlatform.value.firmware = firmware;
}
emitter?.emit("snackbarShow", {
@@ -75,8 +75,8 @@ function deleteFirmware() {
.deleteFirmware({ firmware: selectedFirmware.value, deleteFromFs: false })
.then(() => {
if (selectedPlatform.value) {
selectedPlatform.value.firmware_files =
selectedPlatform.value.firmware_files?.filter(
selectedPlatform.value.firmware =
selectedPlatform.value.firmware?.filter(
(firmware) => !selectedFirmware.value.includes(firmware)
);
}
@@ -91,12 +91,12 @@ function deleteFirmware() {
}
function allFirmwareSelected() {
if (selectedPlatform.value?.firmware_files?.length == 0) {
if (selectedPlatform.value?.firmware?.length == 0) {
return false;
}
return (
selectedFirmware.value.length ===
selectedPlatform.value?.firmware_files?.length
selectedPlatform.value?.firmware?.length
);
}
@@ -104,7 +104,7 @@ function selectAllFirmware() {
if (allFirmwareSelected()) {
selectedFirmware.value = [];
} else {
selectedFirmware.value = selectedPlatform.value?.firmware_files ?? [];
selectedFirmware.value = selectedPlatform.value?.firmware ?? [];
}
}
</script>
@@ -223,14 +223,14 @@ function selectAllFirmware() {
<v-card-text
class="my-4 py-0"
v-if="
selectedPlatform?.firmware_files != undefined &&
selectedPlatform?.firmware_files?.length > 0
selectedPlatform?.firmware != undefined &&
selectedPlatform?.firmware?.length > 0
"
>
<v-list rounded="0" class="pa-0">
<v-list-item
class="px-3"
v-for="firmware in selectedPlatform?.firmware_files ?? []"
v-for="firmware in selectedPlatform?.firmware ?? []"
:key="firmware.id"
>
<template v-slot:prepend>
@@ -286,8 +286,8 @@ function selectAllFirmware() {
variant="text"
:disabled="
!(
selectedPlatform?.firmware_files != undefined &&
selectedPlatform?.firmware_files?.length > 0
selectedPlatform?.firmware != undefined &&
selectedPlatform?.firmware?.length > 0
)
"
>

View File

@@ -1,10 +0,0 @@
import api from "@/services/api/index";
export const stateApi = api;
async function getFilters(): Promise<{ data: { genres: string[] } }> {
return api.get("/filters");
}
export default {
getFilters,
};

View File

@@ -1,18 +1,40 @@
import axios from "axios";
import cookie from "js-cookie";
import router from "@/plugins/router";
import { debounce } from "lodash";
const api = axios.create({ baseURL: "/api", timeout: 120000 });
// Set CSRF header for all requests
const inflightRequests = new Set();
const networkQuiesced = debounce(() => {
document.dispatchEvent(new CustomEvent("network-quiesced"));
}, 300);
api.interceptors.request.use((config) => {
// Add request to set of inflight requests
inflightRequests.add(config.url);
// Cancel debounced networkQuiesced since a new request just came in
networkQuiesced.cancel();
// Set CSRF header for all requests
config.headers["x-csrftoken"] = cookie.get("csrftoken");
return config;
});
api.interceptors.response.use(
(response) => response,
(response) => {
// Remove request from set of inflight requests
inflightRequests.delete(response.config.url);
// If there are no more inflight requests, fetch app-wide data
if (inflightRequests.size === 0) {
networkQuiesced();
}
return response;
},
(error) => {
if (error.response?.status === 403) {
router.push({

View File

@@ -20,14 +20,12 @@ import DeleteUserDialog from "@/components/Dialog/User/DeleteUser.vue";
import EditUserDialog from "@/components/Dialog/User/EditUser.vue";
import Drawer from "@/components/Drawer/Base.vue";
import platformApi from "@/services/api/platform";
import userApi from "@/services/api/user";
import storeAuth from "@/stores/auth";
import storePlatforms from "@/stores/platforms";
import storeScanning from "@/stores/scanning";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, onMounted, ref } from "vue";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
// Props
@@ -35,7 +33,6 @@ const { mdAndDown } = useDisplay();
const scanningStore = storeScanning();
const { scanning } = storeToRefs(scanningStore);
const platformsStore = storePlatforms();
const auth = storeAuth();
const refreshView = ref(0);
// Event listeners bus
@@ -47,27 +44,6 @@ emitter?.on("refreshDrawer", async () => {
emitter?.on("refreshView", async () => {
refreshView.value = refreshView.value + 1;
});
// Functions
onMounted(() => {
platformApi
.getPlatforms()
.then(({ data: platforms }) => {
platformsStore.set(platforms);
})
.catch((error) => {
console.error(error);
});
userApi
.fetchCurrentUser()
.then(({ data: user }) => {
auth.setUser(user);
})
.catch((error) => {
console.error(error);
});
});
</script>
<template>