mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
standarized GET endpoints for roms and platforms
This commit is contained in:
23
backend/decorators/database.py
Normal file
23
backend/decorators/database.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import functools
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from logger.logger import log
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
|
||||
|
||||
def begin_session(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if hasattr(kwargs, "session"):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
try:
|
||||
with args[0].session.begin() as s:
|
||||
return func(*args, **kwargs, session=s)
|
||||
except ProgrammingError as e:
|
||||
log.critical(str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
||||
)
|
||||
|
||||
return wrapper
|
||||
@@ -1,7 +1,7 @@
|
||||
from pathlib import Path
|
||||
|
||||
from config.config_manager import config_manager as cm
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses.assets import (
|
||||
SaveSchema,
|
||||
StateSchema,
|
||||
@@ -9,7 +9,7 @@ from endpoints.responses.assets import (
|
||||
UploadedStatesResponse,
|
||||
)
|
||||
from fastapi import APIRouter, File, HTTPException, Request, UploadFile, status
|
||||
from handler import dbh, romh
|
||||
from handler import dbh, fsromh
|
||||
from handler.scan_handler import scan_save, scan_state
|
||||
from logger.logger import log
|
||||
|
||||
@@ -42,7 +42,7 @@ def upload_saves(
|
||||
detail="No saves were uploaded",
|
||||
)
|
||||
|
||||
saves_path = romh.build_upload_file_path(
|
||||
saves_path = fsromh.build_upload_file_path(
|
||||
rom.platform.fs_slug, folder=cm.config.SAVES_FOLDER_NAME
|
||||
)
|
||||
|
||||
@@ -90,7 +90,7 @@ async def delete_saves(request: Request) -> list[SaveSchema]:
|
||||
log.info(f"Deleting {save.file_name} from filesystem")
|
||||
|
||||
try:
|
||||
romh.remove_file(file_name=save.file_name, file_path=save.file_path)
|
||||
fsromh.remove_file(file_name=save.file_name, file_path=save.file_path)
|
||||
except FileNotFoundError:
|
||||
error = f"Save file {save.file_name} not found for platform {save.platform_slug}"
|
||||
log.error(error)
|
||||
@@ -113,7 +113,7 @@ def upload_states(
|
||||
detail="No states were uploaded",
|
||||
)
|
||||
|
||||
states_path = romh.build_upload_file_path(
|
||||
states_path = fsromh.build_upload_file_path(
|
||||
rom.platform.fs_slug, folder=cm.config.STATES_FOLDER_NAME
|
||||
)
|
||||
|
||||
@@ -160,7 +160,7 @@ async def delete_states(request: Request) -> list[StateSchema]:
|
||||
if delete_from_fs:
|
||||
log.info(f"Deleting {state.file_name} from filesystem")
|
||||
try:
|
||||
romh.remove_file(file_name=state.file_name, file_path=state.file_path)
|
||||
fsromh.remove_file(file_name=state.file_name, file_path=state.file_path)
|
||||
except FileNotFoundError:
|
||||
error = f"Save file {state.file_name} not found for platform {state.platform_slug}"
|
||||
log.error(error)
|
||||
|
||||
33
backend/endpoints/config.py
Normal file
33
backend/endpoints/config.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from config.config_manager import config_manager as cm
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses import MessageResponse
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@protected_route(router.put, "/config/system/platforms", ["platforms.write"])
|
||||
async def add_platform_binding(request: Request) -> MessageResponse:
|
||||
"""Add platform binding to the configuration"""
|
||||
|
||||
data = await request.form()
|
||||
|
||||
fs_slug = data.get("fs_slug")
|
||||
slug = data.get("slug")
|
||||
|
||||
cm.add_binding(fs_slug, slug)
|
||||
|
||||
return {"msg": f"{fs_slug} binded to: {slug} successfully!"}
|
||||
|
||||
|
||||
@protected_route(router.patch, "/config/system/platforms", ["platforms.write"])
|
||||
async def delete_platform_binding(request: Request) -> MessageResponse:
|
||||
"""Delete platform binding from the configuration"""
|
||||
|
||||
data = await request.form()
|
||||
|
||||
fs_slug = data.get("fs_slug")
|
||||
|
||||
cm.remove_binding(fs_slug)
|
||||
|
||||
return {"msg": f"{fs_slug} bind removed successfully!"}
|
||||
@@ -2,14 +2,14 @@ import secrets
|
||||
from typing import Annotated
|
||||
|
||||
from config import ROMM_AUTH_ENABLED
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.forms.identity import UserForm
|
||||
from endpoints.responses import MessageResponse
|
||||
from endpoints.responses.identity import UserSchema
|
||||
from exceptions.auth_exceptions import AuthCredentialsException, DisabledException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.security.http import HTTPBasic
|
||||
from handler import authh, dbh, resourceh
|
||||
from handler import authh, dbh, fsresourceh
|
||||
from handler.redis_handler import cache
|
||||
from models.user import Role, User
|
||||
|
||||
@@ -207,7 +207,7 @@ def update_user(
|
||||
cleaned_data["enabled"] = form_data.enabled # type: ignore[assignment]
|
||||
|
||||
if form_data.avatar is not None:
|
||||
cleaned_data["avatar_path"], avatar_user_path = resourceh.build_avatar_path(
|
||||
cleaned_data["avatar_path"], avatar_user_path = fsresourceh.build_avatar_path(
|
||||
form_data.avatar.filename, form_data.username
|
||||
)
|
||||
file_location = f"{avatar_user_path}/{form_data.avatar.filename}"
|
||||
|
||||
@@ -1,80 +1,82 @@
|
||||
from config.config_manager import config_manager as cm
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses import MessageResponse
|
||||
from endpoints.responses.platform import PlatformSchema
|
||||
from fastapi import APIRouter, HTTPException, Request, status
|
||||
from handler import dbh
|
||||
from handler import dbplatformh
|
||||
from logger.logger import log
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@protected_route(router.post, "/platforms", ["platforms.write"])
|
||||
def add_platform(request: Request) -> MessageResponse:
|
||||
"""Create platform endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
|
||||
Returns:
|
||||
MessageResponse: Standard message response
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@protected_route(router.get, "/platforms", ["platforms.read"])
|
||||
def get_platforms(request: Request) -> list[PlatformSchema]:
|
||||
"""Get platforms endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
id (int, optional): Platform id. Defaults to None.
|
||||
|
||||
Returns:
|
||||
list[PlatformSchema]: All platforms in the database
|
||||
list[PlatformSchema]: List of platforms
|
||||
"""
|
||||
|
||||
return dbh.get_platform()
|
||||
return dbplatformh.get_platforms()
|
||||
|
||||
|
||||
@protected_route(router.get, "/platforms/{id}", ["platforms.read"])
|
||||
def get_platforms(request: Request, id: int = None) -> PlatformSchema:
|
||||
"""Get platform endpoint
|
||||
def get_platforms(request: Request, id: int) -> PlatformSchema:
|
||||
"""Get platforms endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
id (int, optional): Platform id. Defaults to None.
|
||||
|
||||
Returns:
|
||||
PlatformSchema: Platform
|
||||
"""
|
||||
|
||||
return dbplatformh.get_platforms(id)
|
||||
|
||||
|
||||
@protected_route(router.put, "/platforms/{id}", ["platforms.write"])
|
||||
def update_platform(request: Request, form) -> MessageResponse:
|
||||
"""Update platform endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
|
||||
Returns:
|
||||
PlatformSchema: All platforms in the database
|
||||
MessageResponse: Standard message response
|
||||
"""
|
||||
|
||||
return dbh.get_platform(id)
|
||||
pass
|
||||
|
||||
|
||||
@protected_route(router.delete, "/platforms/{id}", ["platforms.write"])
|
||||
def delete_platforms(request: Request, id: int) -> MessageResponse:
|
||||
"""Detele platform from database [and filesystem]"""
|
||||
|
||||
platform = dbh.get_platform(id)
|
||||
platform = dbplatformh.get_platforms(id)
|
||||
if not platform:
|
||||
error = f"Platform {platform.name} - [{platform.fs_slug}] not found"
|
||||
log.error(error)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)
|
||||
|
||||
log.info(f"Deleting {platform.name} [{platform.fs_slug}] from database")
|
||||
dbh.delete_platform(platform.id)
|
||||
dbplatformh.delete_platform(id)
|
||||
|
||||
return {"msg": f"{platform.name} - [{platform.fs_slug}] deleted successfully!"}
|
||||
|
||||
|
||||
@protected_route(router.put, "/config/system/platforms", ["platforms.write"])
|
||||
async def add_platform_binding(request: Request) -> MessageResponse:
|
||||
"""Add platform binding to the configuration"""
|
||||
|
||||
data = await request.form()
|
||||
|
||||
fs_slug = data.get("fs_slug")
|
||||
slug = data.get("slug")
|
||||
|
||||
cm.add_binding(fs_slug, slug)
|
||||
|
||||
return {"msg": f"{fs_slug} binded to: {slug} successfully!"}
|
||||
|
||||
|
||||
@protected_route(router.patch, "/config/system/platforms", ["platforms.write"])
|
||||
async def delete_platform_binding(request: Request) -> MessageResponse:
|
||||
"""Delete platform binding from the configuration"""
|
||||
|
||||
data = await request.form()
|
||||
|
||||
fs_slug = data.get("fs_slug")
|
||||
|
||||
cm.remove_binding(fs_slug)
|
||||
|
||||
return {"msg": f"{fs_slug} bind removed successfully!"}
|
||||
|
||||
@@ -4,7 +4,7 @@ from stat import S_IFREG
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from config import LIBRARY_BASE_PATH
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses import MessageResponse
|
||||
from endpoints.responses.rom import (
|
||||
CustomStreamingResponse,
|
||||
@@ -17,7 +17,7 @@ from fastapi import APIRouter, File, HTTPException, Query, Request, UploadFile,
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi_pagination.cursor import CursorPage, CursorParams
|
||||
from fastapi_pagination.ext.sqlalchemy import paginate
|
||||
from handler import asseth, dbh, romh
|
||||
from handler import dbplatformh, dbromh, fsasseth, fsresourceh, fsromh
|
||||
from logger.logger import log
|
||||
from models import Rom
|
||||
from stream_zip import ZIP_64, stream_zip # type: ignore[import]
|
||||
@@ -25,8 +25,49 @@ from stream_zip import ZIP_64, stream_zip # type: ignore[import]
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@protected_route(router.post, "/roms", ["roms.write"])
|
||||
def add_rom(request: Request) -> MessageResponse:
|
||||
"""Create rom endpoint
|
||||
|
||||
Args
|
||||
request (Request): Fastapi Request object
|
||||
|
||||
Returns:
|
||||
MessageResponse: Standard message response
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@protected_route(router.get, "/roms", ["roms.read"])
|
||||
def get_roms(
|
||||
request: Request,
|
||||
platform_id: int = None,
|
||||
size: int = 60,
|
||||
cursor: str = "",
|
||||
search_term: str = "",
|
||||
order_by: str = "name",
|
||||
order_dir: str = "asc",
|
||||
) -> CursorPage[RomSchema]:
|
||||
"""Get roms endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
id (int, optional): Rom internal id
|
||||
|
||||
Returns:
|
||||
EnhancedRomSchema: Rom stored in RomM's database
|
||||
"""
|
||||
|
||||
with dbromh.session.begin() as session:
|
||||
cursor_params = CursorParams(size=size, cursor=cursor)
|
||||
qq = dbromh.get_roms(platform_id, search_term, order_by, order_dir)
|
||||
return paginate(session, qq, cursor_params)
|
||||
|
||||
|
||||
@protected_route(router.get, "/roms/{id}", ["roms.read"])
|
||||
def rom(request: Request, id: int) -> EnhancedRomSchema:
|
||||
def get_rom(request: Request, id: int) -> EnhancedRomSchema:
|
||||
"""Get rom endpoint
|
||||
|
||||
Args:
|
||||
@@ -37,61 +78,12 @@ def rom(request: Request, id: int) -> EnhancedRomSchema:
|
||||
EnhancedRomSchema: Rom stored in RomM's database
|
||||
"""
|
||||
|
||||
return dbh.get_rom(id)
|
||||
|
||||
|
||||
@protected_route(router.get, "/roms-recent", ["roms.read"])
|
||||
def recent_roms(request: Request) -> list[RomSchema]:
|
||||
"""Get recent roms endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
|
||||
Returns:
|
||||
list[RomSchema]: List of the last 15 stored roms in RomM's database
|
||||
"""
|
||||
|
||||
return dbh.get_recent_roms()
|
||||
|
||||
|
||||
@protected_route(router.get, "/platforms/{platform_slug}/roms", ["roms.read"])
|
||||
def roms(
|
||||
request: Request,
|
||||
platform_slug: str,
|
||||
size: int = 60,
|
||||
cursor: str = "",
|
||||
search_term: str = "",
|
||||
) -> CursorPage[RomSchema]:
|
||||
"""Get all roms for a specific platform endpoint (paginated)
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
platform_slug (str): Platform slug
|
||||
size (int, optional): Size of each page. Defaults to 60.
|
||||
cursor (str, optional): Cursor string. Defaults to "".
|
||||
search_term (str, optional): Filter to search roms. Defaults to "".
|
||||
|
||||
Returns:
|
||||
CursorPage[RomSchema]: Paged list of roms
|
||||
"""
|
||||
|
||||
with dbh.session.begin() as session:
|
||||
cursor_params = CursorParams(size=size, cursor=cursor)
|
||||
qq = dbh.get_roms(platform_slug)
|
||||
|
||||
if search_term:
|
||||
return paginate(
|
||||
session,
|
||||
qq.filter(Rom.file_name.ilike(f"%{search_term}%")),
|
||||
cursor_params,
|
||||
)
|
||||
|
||||
return paginate(session, qq, cursor_params)
|
||||
return dbromh.get_roms(id)
|
||||
|
||||
|
||||
@protected_route(router.put, "/roms/upload", ["roms.write"])
|
||||
def upload_roms(
|
||||
request: Request, platform_slug: str, roms: list[UploadFile] = File(...)
|
||||
request: Request, platform_id: str, roms: list[UploadFile] = File(...)
|
||||
) -> UploadRomResponse:
|
||||
"""Upload roms endpoint (one or more at the same time)
|
||||
|
||||
@@ -107,7 +99,7 @@ def upload_roms(
|
||||
UploadRomResponse: Standard message response
|
||||
"""
|
||||
|
||||
platform_fs_slug = dbh.get_platform(platform_slug).fs_slug
|
||||
platform_fs_slug = dbromh.get_platforms(platform_id).fs_slug
|
||||
log.info(f"Uploading roms to {platform_fs_slug}")
|
||||
if roms is None:
|
||||
log.error("No roms were uploaded")
|
||||
@@ -116,13 +108,13 @@ def upload_roms(
|
||||
detail="No roms were uploaded",
|
||||
)
|
||||
|
||||
roms_path = romh.build_upload_file_path(platform_fs_slug)
|
||||
roms_path = fsromh.build_upload_file_path(platform_fs_slug)
|
||||
|
||||
uploaded_roms = []
|
||||
skipped_roms = []
|
||||
|
||||
for rom in roms:
|
||||
if romh.file_exists(roms_path, rom.filename):
|
||||
if fsromh.file_exists(roms_path, rom.filename):
|
||||
log.warning(f" - Skipping {rom.filename} since the file already exists")
|
||||
skipped_roms.append(rom.filename)
|
||||
continue
|
||||
@@ -163,7 +155,7 @@ def download_rom(
|
||||
CustomStreamingResponse: Streams a file for multi-part roms
|
||||
"""
|
||||
|
||||
rom = dbh.get_rom(id)
|
||||
rom = dbromh.get_roms(id)
|
||||
rom_path = f"{LIBRARY_BASE_PATH}/{rom.full_path}"
|
||||
|
||||
if not rom.multi:
|
||||
@@ -219,8 +211,8 @@ async def update_rom(
|
||||
|
||||
data = await request.form()
|
||||
|
||||
db_rom = dbh.get_rom(id)
|
||||
platform_fs_slug = dbh.get_platform(db_rom.platform_slug).fs_slug
|
||||
db_rom = dbromh.get_roms(id)
|
||||
platform_fs_slug = dbplatformh.get_platforms(db_rom.platform_id).fs_slug
|
||||
|
||||
cleaned_data = {}
|
||||
cleaned_data["igdb_id"] = data.get("igdb_id", db_rom.igdb_id) or None
|
||||
@@ -242,7 +234,7 @@ async def update_rom(
|
||||
|
||||
try:
|
||||
if db_rom.file_name != fs_safe_file_name:
|
||||
romh.rename_file(
|
||||
fsromh.rename_file(
|
||||
old_name=db_rom.file_name,
|
||||
new_name=fs_safe_file_name,
|
||||
file_path=db_rom.file_path,
|
||||
@@ -254,11 +246,11 @@ async def update_rom(
|
||||
)
|
||||
|
||||
cleaned_data["file_name"] = fs_safe_file_name
|
||||
cleaned_data["file_name_no_tags"] = romh.get_file_name_with_no_tags(
|
||||
cleaned_data["file_name_no_tags"] = fsromh.get_file_name_with_no_tags(
|
||||
fs_safe_file_name
|
||||
)
|
||||
cleaned_data.update(
|
||||
asseth.get_rom_cover(
|
||||
fsresourceh.get_rom_cover(
|
||||
overwrite=True,
|
||||
fs_slug=platform_fs_slug,
|
||||
rom_name=cleaned_data["name"],
|
||||
@@ -267,7 +259,7 @@ async def update_rom(
|
||||
)
|
||||
|
||||
cleaned_data.update(
|
||||
asseth.get_rom_screenshots(
|
||||
fsasseth.get_rom_screenshots(
|
||||
fs_slug=platform_fs_slug,
|
||||
rom_name=cleaned_data["name"],
|
||||
url_screenshots=cleaned_data.get("url_screenshots", []),
|
||||
@@ -276,7 +268,7 @@ async def update_rom(
|
||||
|
||||
if artwork is not None:
|
||||
file_ext = artwork.filename.split(".")[-1]
|
||||
path_cover_l, path_cover_s, artwork_path = asseth.build_artwork_path(
|
||||
path_cover_l, path_cover_s, artwork_path = fsresourceh.build_artwork_path(
|
||||
cleaned_data["name"], platform_fs_slug, file_ext
|
||||
)
|
||||
|
||||
@@ -292,12 +284,12 @@ async def update_rom(
|
||||
with open(file_location_l, "wb+") as artwork_l:
|
||||
artwork_l.write(artwork_file)
|
||||
|
||||
dbh.update_rom(id, cleaned_data)
|
||||
dbromh.update_rom(id, cleaned_data)
|
||||
|
||||
return dbh.get_rom(id)
|
||||
return dbromh.get_roms(id)
|
||||
|
||||
|
||||
def _delete_single_rom(rom_id: int, delete_from_fs: bool = False) -> Rom:
|
||||
def _delete_single_rom(id: int, delete_from_fs: bool = False) -> Rom:
|
||||
"""Auxiliar function to delete one single rom at once
|
||||
|
||||
Args:
|
||||
@@ -312,19 +304,19 @@ def _delete_single_rom(rom_id: int, delete_from_fs: bool = False) -> Rom:
|
||||
Rom: Rom object
|
||||
"""
|
||||
|
||||
rom = dbh.get_rom(rom_id)
|
||||
rom = dbromh.get_rosm(id)
|
||||
if not rom:
|
||||
error = f"Rom with id {rom_id} not found"
|
||||
error = f"Rom with id {id} not found"
|
||||
log.error(error)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)
|
||||
|
||||
log.info(f"Deleting {rom.file_name} from database")
|
||||
dbh.delete_rom(rom_id)
|
||||
dbromh.delete_rom(id)
|
||||
|
||||
if delete_from_fs:
|
||||
log.info(f"Deleting {rom.file_name} from filesystem")
|
||||
try:
|
||||
romh.remove_file(file_name=rom.file_name, file_path=rom.file_path)
|
||||
fsromh.remove_file(file_name=rom.file_name, file_path=rom.file_path)
|
||||
except FileNotFoundError:
|
||||
error = (
|
||||
f"Rom file {rom.file_name} not found for platform {rom.platform_slug}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import emoji
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses.search import RomSearchResponse
|
||||
from fastapi import APIRouter, Request
|
||||
from handler import dbh, igdbh
|
||||
|
||||
@@ -7,7 +7,7 @@ from exceptions.fs_exceptions import (
|
||||
FolderStructureNotMatchException,
|
||||
RomsNotFoundException,
|
||||
)
|
||||
from handler import asseth, dbh, platformh, resourceh, romh, socketh
|
||||
from handler import fsasseth, dbh, fsplatformh, fsresourceh, fsromh, socketh
|
||||
from handler.redis_handler import high_prio_queue, redis_url
|
||||
from handler.scan_handler import (
|
||||
scan_platform,
|
||||
@@ -43,7 +43,7 @@ async def scan_platforms(
|
||||
|
||||
# Scanning file system
|
||||
try:
|
||||
fs_platforms: list[str] = platformh.get_platforms()
|
||||
fs_platforms: list[str] = fsplatformh.get_platforms()
|
||||
except FolderStructureNotMatchException as e:
|
||||
log.error(e)
|
||||
await sm.emit("scan:done_ko", e.message)
|
||||
@@ -71,7 +71,7 @@ async def scan_platforms(
|
||||
|
||||
# Scanning roms
|
||||
try:
|
||||
fs_roms = romh.get_roms(platform.fs_slug)
|
||||
fs_roms = fsromh.get_roms(platform.fs_slug)
|
||||
except RomsNotFoundException as e:
|
||||
log.error(e)
|
||||
continue
|
||||
@@ -106,7 +106,7 @@ async def scan_platforms(
|
||||
},
|
||||
)
|
||||
|
||||
fs_assets = asseth.get_assets(platform.fs_slug)
|
||||
fs_assets = fsasseth.get_assets(platform.fs_slug)
|
||||
|
||||
# Scanning saves
|
||||
log.info(f"\t · {len(fs_assets['saves'])} saves found")
|
||||
@@ -190,7 +190,7 @@ async def scan_platforms(
|
||||
dbh.purge_roms(platform.id, [rom["file_name"] for rom in fs_roms])
|
||||
|
||||
# Scanning screenshots outside platform folders
|
||||
fs_screenshots = asseth.get_screenshots()
|
||||
fs_screenshots = fsasseth.get_screenshots()
|
||||
log.info("Screenshots")
|
||||
log.info(f" · {len(fs_screenshots)} screenshots found")
|
||||
for fs_platform, fs_screenshot_filename in fs_screenshots:
|
||||
@@ -231,7 +231,7 @@ async def scan_handler(_sid: str, options: dict):
|
||||
"""
|
||||
|
||||
log.info(emoji.emojize(":magnifying_glass_tilted_right: Scanning "))
|
||||
resourceh.store_default_resources()
|
||||
fsresourceh.store_default_resources()
|
||||
|
||||
platform_slugs = options.get("platforms", [])
|
||||
complete_rescan = options.get("completeRescan", False)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses import MessageResponse
|
||||
from fastapi import APIRouter, Request
|
||||
from tasks.update_mame_xml import update_mame_xml_task
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from config import ROMM_HOST
|
||||
from decorators.oauth import protected_route
|
||||
from decorators.auth import protected_route
|
||||
from endpoints.responses.webrcade import (
|
||||
WEBRCADE_SLUG_TO_TYPE_MAP,
|
||||
WEBRCADE_SUPPORTED_PLATFORM_SLUGS,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from handler.auth_handler.auth_handler import AuthHandler, OAuthHandler
|
||||
from handler.db_handler import DBHandler
|
||||
from handler.fs_handler.assets_handler import AssetsHandler
|
||||
from handler.fs_handler.platforms_handler import PlatformsHandler
|
||||
from handler.fs_handler.resources_handler import ResourceHandler
|
||||
from handler.fs_handler.roms_handler import RomsHandler
|
||||
from handler.db_handler.db_platforms_handler import DBPlatformsHandler
|
||||
from handler.db_handler.db_roms_handler import DBRomsHandler
|
||||
from handler.fs_handler.fs_assets_handler import FSAssetsHandler
|
||||
from handler.fs_handler.fs_platforms_handler import FSPlatformsHandler
|
||||
from handler.fs_handler.fs_resources_handler import FSResourceHandler
|
||||
from handler.fs_handler.fs_roms_handler import FSRomsHandler
|
||||
from handler.gh_handler import GHHandler
|
||||
from handler.igdb_handler import IGDBHandler
|
||||
from handler.sgdb_handler import SGDBHandler
|
||||
@@ -11,12 +12,17 @@ from handler.socket_handler import SocketHandler
|
||||
|
||||
igdbh = IGDBHandler()
|
||||
sgdbh = SGDBHandler()
|
||||
dbh = DBHandler()
|
||||
ghh = GHHandler()
|
||||
authh = AuthHandler()
|
||||
oauthh = OAuthHandler()
|
||||
socketh = SocketHandler()
|
||||
platformh = PlatformsHandler()
|
||||
romh = RomsHandler()
|
||||
asseth = AssetsHandler()
|
||||
resourceh = ResourceHandler()
|
||||
dbplatformh = DBPlatformsHandler()
|
||||
dbromh = DBRomsHandler()
|
||||
fsplatformh = FSPlatformsHandler()
|
||||
fsromh = FSRomsHandler()
|
||||
fsasseth = FSAssetsHandler()
|
||||
fsresourceh = FSResourceHandler()
|
||||
|
||||
|
||||
from handler.db_handler.db_handler import DBHandler
|
||||
dbh = DBHandler()
|
||||
|
||||
0
backend/handler/db_handler/__init__.py
Normal file
0
backend/handler/db_handler/__init__.py
Normal file
@@ -1,11 +1,7 @@
|
||||
import functools
|
||||
|
||||
from config.config_manager import ConfigManager
|
||||
from fastapi import HTTPException, status
|
||||
from logger.logger import log
|
||||
from models import Platform, Role, Rom, Save, Screenshot, State, User
|
||||
from sqlalchemy import and_, create_engine, delete, func, or_, select, update
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
from decorators.database import begin_session
|
||||
from models import Role, Rom, Save, Screenshot, State, User
|
||||
from sqlalchemy import and_, create_engine, delete, func, select, update
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
|
||||
@@ -14,84 +10,6 @@ class DBHandler:
|
||||
self.engine = create_engine(ConfigManager.get_db_engine(), pool_pre_ping=True)
|
||||
self.session = sessionmaker(bind=self.engine, expire_on_commit=False)
|
||||
|
||||
@staticmethod
|
||||
def begin_session(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if hasattr(kwargs, "session"):
|
||||
return func(*args, session=kwargs.get("session"))
|
||||
|
||||
try:
|
||||
with args[0].session.begin() as s:
|
||||
return func(*args, session=s)
|
||||
except ProgrammingError as e:
|
||||
log.critical(str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
||||
# ========= Platforms =========
|
||||
@begin_session
|
||||
def add_platform(self, platform: Platform, session: Session = None):
|
||||
return session.merge(platform)
|
||||
|
||||
@begin_session
|
||||
def get_platform(self, id: int = None, session: Session = None):
|
||||
return (
|
||||
session.scalars(select(Platform).order_by(Platform.slug.asc()))
|
||||
.unique()
|
||||
.all()
|
||||
if not id
|
||||
else session.get(Platform, id)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def delete_platform(self, slug: str, session: Session = None):
|
||||
# Remove all roms from that platforms first
|
||||
session.execute(
|
||||
delete(Rom)
|
||||
.where(Rom.platform_slug == slug)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
return session.execute(
|
||||
delete(Platform)
|
||||
.where(Platform.slug == slug)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def get_rom_count(self, platform_id: int, session: Session = None):
|
||||
return session.scalar(
|
||||
select(func.count()).select_from(Rom).filter_by(platform_id=platform_id)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def purge_platforms(self, platforms: list[str], session: Session = None):
|
||||
session.execute(
|
||||
delete(Save)
|
||||
.where(Save.platform_slug.not_in(platforms))
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
session.execute(
|
||||
delete(State)
|
||||
.where(State.platform_slug.not_in(platforms))
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
return session.execute(
|
||||
delete(Platform)
|
||||
.where(or_(Platform.fs_slug.not_in(platforms), Platform.slug.is_(None)))
|
||||
.where(
|
||||
select(func.count())
|
||||
.select_from(Rom)
|
||||
.filter_by(platform_slug=Platform.slug)
|
||||
.as_scalar()
|
||||
== 0
|
||||
)
|
||||
.execution_options(synchronize_session="fetch")
|
||||
)
|
||||
|
||||
# ========= Roms =========
|
||||
@begin_session
|
||||
def add_rom(self, rom: Rom, session: Session = None):
|
||||
69
backend/handler/db_handler/db_platforms_handler.py
Normal file
69
backend/handler/db_handler/db_platforms_handler.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from models import Platform, Rom, Save, State
|
||||
from sqlalchemy import delete, func, or_, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from decorators.database import begin_session
|
||||
from handler.db_handler.db_handler import DBHandler
|
||||
|
||||
|
||||
class DBPlatformsHandler(DBHandler):
|
||||
@begin_session
|
||||
def add_platform(self, platform: Platform, session: Session = None):
|
||||
return session.merge(platform)
|
||||
|
||||
@begin_session
|
||||
def get_platforms(self, id: int = None, session: Session = None):
|
||||
return (
|
||||
session.get(Platform, id)
|
||||
if id
|
||||
else (
|
||||
session.scalars(select(Platform).order_by(Platform.name.asc()))
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def delete_platform(self, slug: str, session: Session = None):
|
||||
# Remove all roms from that platforms first
|
||||
session.execute(
|
||||
delete(Rom)
|
||||
.where(Rom.platform_slug == slug)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
return session.execute(
|
||||
delete(Platform)
|
||||
.where(Platform.slug == slug)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def get_rom_count(self, platform_id: int, session: Session = None):
|
||||
return session.scalar(
|
||||
select(func.count()).select_from(Rom).filter_by(platform_id=platform_id)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def purge_platforms(self, platforms: list[str], session: Session = None):
|
||||
session.execute(
|
||||
delete(Save)
|
||||
.where(Save.platform_slug.not_in(platforms))
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
session.execute(
|
||||
delete(State)
|
||||
.where(State.platform_slug.not_in(platforms))
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
return session.execute(
|
||||
delete(Platform)
|
||||
.where(or_(Platform.fs_slug.not_in(platforms), Platform.slug.is_(None)))
|
||||
.where(
|
||||
select(func.count())
|
||||
.select_from(Rom)
|
||||
.filter_by(platform_slug=Platform.slug)
|
||||
.as_scalar()
|
||||
== 0
|
||||
)
|
||||
.execution_options(synchronize_session="fetch")
|
||||
)
|
||||
76
backend/handler/db_handler/db_roms_handler.py
Normal file
76
backend/handler/db_handler/db_roms_handler.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from decorators.database import begin_session
|
||||
from handler.db_handler.db_handler import DBHandler
|
||||
from models import Rom
|
||||
from sqlalchemy import and_, delete, func, select, update
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
class DBRomsHandler(DBHandler):
|
||||
@staticmethod
|
||||
def _filter(data, platform_id, search_term):
|
||||
if platform_id:
|
||||
data = data.filter_by(platform_id=platform_id)
|
||||
if search_term:
|
||||
data = data.filter(Rom.file_name.ilike(f"%{search_term}%"))
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _order(data, order_by, order_dir):
|
||||
if order_by == "name":
|
||||
_column = func.lower(Rom.name)
|
||||
elif order_by == "id":
|
||||
_column = Rom.id
|
||||
else:
|
||||
_column = func.lower(Rom.name)
|
||||
|
||||
if order_dir == "asc":
|
||||
return data.order_by(_column.asc())
|
||||
elif order_dir == "desc":
|
||||
return data.order_by(_column.desc())
|
||||
else:
|
||||
return data.order_by(_column.asc())
|
||||
|
||||
@begin_session
|
||||
def get_roms(
|
||||
self,
|
||||
id: int = None,
|
||||
platform_id: int = None,
|
||||
search_term: str = "",
|
||||
order_by: str = "name",
|
||||
order_dir: str = "asc",
|
||||
session: Session = None,
|
||||
):
|
||||
return (
|
||||
session.get(Rom, id)
|
||||
if id
|
||||
else self._order(
|
||||
self._filter(select(Rom), platform_id, search_term),
|
||||
order_by,
|
||||
order_dir,
|
||||
)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def update_rom(self, id: int, data: dict, session: Session = None):
|
||||
return session.execute(
|
||||
update(Rom)
|
||||
.where(Rom.id == id)
|
||||
.values(**data)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def delete_rom(self, id: int, session: Session = None):
|
||||
return session.execute(
|
||||
delete(Rom)
|
||||
.where(Rom.id == id)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def purge_roms(self, platform_id: int, roms: list[str], session: Session = None):
|
||||
return session.execute(
|
||||
delete(Rom)
|
||||
.where(and_(Rom.platform_id == platform_id, Rom.file_name.not_in(roms)))
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
@@ -10,7 +10,7 @@ from handler.fs_handler import RESOURCES_BASE_PATH
|
||||
from handler.fs_handler.fs_handler import FSHandler
|
||||
|
||||
|
||||
class AssetsHandler(FSHandler):
|
||||
class FSAssetsHandler(FSHandler):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -6,7 +6,7 @@ from exceptions.fs_exceptions import FolderStructureNotMatchException
|
||||
from handler.fs_handler.fs_handler import FSHandler
|
||||
|
||||
|
||||
class PlatformsHandler(FSHandler):
|
||||
class FSPlatformsHandler(FSHandler):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -23,7 +23,7 @@ from handler.fs_handler.fs_handler import FSHandler
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class ResourceHandler(FSHandler):
|
||||
class FSResourceHandler(FSHandler):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -18,7 +18,7 @@ from handler.fs_handler import (
|
||||
from handler.fs_handler.fs_handler import FSHandler
|
||||
|
||||
|
||||
class RomsHandler(FSHandler):
|
||||
class FSRomsHandler(FSHandler):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Any
|
||||
|
||||
import emoji
|
||||
from config.config_manager import config_manager as cm
|
||||
from handler import asseth, dbh, igdbh, resourceh, romh
|
||||
from handler import fsasseth, dbh, igdbh, fsresourceh, fsromh
|
||||
from logger.logger import log
|
||||
from models import Platform, Rom, Save, Screenshot, State
|
||||
|
||||
@@ -62,7 +62,7 @@ async def scan_rom(
|
||||
r_igbd_id_search: str = "",
|
||||
overwrite: bool = False,
|
||||
) -> Rom:
|
||||
roms_path = romh.get_fs_structure(platform.fs_slug)
|
||||
roms_path = fsromh.get_fs_structure(platform.fs_slug)
|
||||
|
||||
log.info(f"\t · {r_igbd_id_search or rom_attrs['file_name']}")
|
||||
|
||||
@@ -71,21 +71,21 @@ async def scan_rom(
|
||||
log.info(f"\t\t · {file}")
|
||||
|
||||
# Update properties that don't require IGDB
|
||||
file_size, file_size_units = romh.get_rom_file_size(
|
||||
file_size, file_size_units = fsromh.get_rom_file_size(
|
||||
multi=rom_attrs["multi"],
|
||||
file_name=rom_attrs["file_name"],
|
||||
multi_files=rom_attrs["files"],
|
||||
roms_path=roms_path,
|
||||
)
|
||||
regs, rev, langs, other_tags = romh.parse_tags(rom_attrs["file_name"])
|
||||
regs, rev, langs, other_tags = fsromh.parse_tags(rom_attrs["file_name"])
|
||||
rom_attrs.update(
|
||||
{
|
||||
"file_path": roms_path,
|
||||
"file_name": rom_attrs["file_name"],
|
||||
"file_name_no_tags": romh.get_file_name_with_no_tags(
|
||||
"file_name_no_tags": fsromh.get_file_name_with_no_tags(
|
||||
rom_attrs["file_name"]
|
||||
),
|
||||
"file_extension": romh.parse_file_extension(rom_attrs["file_name"]),
|
||||
"file_extension": fsromh.parse_file_extension(rom_attrs["file_name"]),
|
||||
"file_size": file_size,
|
||||
"file_size_units": file_size_units,
|
||||
"multi": rom_attrs["multi"],
|
||||
@@ -117,7 +117,7 @@ async def scan_rom(
|
||||
|
||||
# Update properties from IGDB
|
||||
rom_attrs.update(
|
||||
resourceh.get_rom_cover(
|
||||
fsresourceh.get_rom_cover(
|
||||
overwrite=overwrite,
|
||||
platform_fs_slug=platform.slug,
|
||||
rom_name=rom_attrs["name"],
|
||||
@@ -125,7 +125,7 @@ async def scan_rom(
|
||||
)
|
||||
)
|
||||
rom_attrs.update(
|
||||
asseth.get_rom_screenshots(
|
||||
fsasseth.get_rom_screenshots(
|
||||
platform_fs_slug=platform.slug,
|
||||
rom_name=rom_attrs["name"],
|
||||
url_screenshots=rom_attrs["url_screenshots"],
|
||||
@@ -138,19 +138,19 @@ async def scan_rom(
|
||||
def _scan_asset(file_name: str, path: str):
|
||||
log.info(f"\t\t · {file_name}")
|
||||
|
||||
file_size = asseth.get_asset_size(file_name=file_name, asset_path=path)
|
||||
file_size = fsasseth.get_asset_size(file_name=file_name, asset_path=path)
|
||||
|
||||
return {
|
||||
"file_path": path,
|
||||
"file_name": file_name,
|
||||
"file_name_no_tags": asseth.get_file_name_with_no_tags(file_name),
|
||||
"file_extension": asseth.parse_file_extension(file_name),
|
||||
"file_name_no_tags": fsasseth.get_file_name_with_no_tags(file_name),
|
||||
"file_extension": fsasseth.parse_file_extension(file_name),
|
||||
"file_size_bytes": file_size,
|
||||
}
|
||||
|
||||
|
||||
def scan_save(platform: Platform, file_name: str, emulator: str = None) -> Save:
|
||||
saves_path = asseth.get_fs_structure(
|
||||
saves_path = fsasseth.get_fs_structure(
|
||||
platform.fs_slug, folder=cm.config.SAVES_FOLDER_NAME
|
||||
)
|
||||
|
||||
@@ -162,7 +162,7 @@ def scan_save(platform: Platform, file_name: str, emulator: str = None) -> Save:
|
||||
|
||||
|
||||
def scan_state(platform: Platform, file_name: str, emulator: str = None) -> State:
|
||||
states_path = asseth.get_fs_structure(
|
||||
states_path = fsasseth.get_fs_structure(
|
||||
platform.fs_slug, folder=cm.config.STATES_FOLDER_NAME
|
||||
)
|
||||
|
||||
@@ -174,7 +174,7 @@ def scan_state(platform: Platform, file_name: str, emulator: str = None) -> Stat
|
||||
|
||||
|
||||
def scan_screenshot(file_name: str, platform: Platform = None) -> Screenshot:
|
||||
screenshots_path = asseth.get_fs_structure(
|
||||
screenshots_path = fsasseth.get_fs_structure(
|
||||
platform.fs_slug, folder=cm.config.SCREENSHOTS_FOLDER_NAME
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import alembic.config
|
||||
import uvicorn
|
||||
from config import DEV_HOST, DEV_PORT, ROMM_AUTH_ENABLED, ROMM_AUTH_SECRET_KEY
|
||||
from endpoints import (assets, heartbeat, identity, oauth, platform, rom,
|
||||
search, tasks, webrcade)
|
||||
search, tasks, webrcade, config)
|
||||
from endpoints.sockets import scan
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -57,6 +57,7 @@ app.include_router(search.router)
|
||||
app.include_router(assets.router)
|
||||
app.include_router(tasks.router)
|
||||
app.include_router(webrcade.router)
|
||||
app.include_router(webrcade.router)
|
||||
|
||||
add_pagination(app)
|
||||
app.mount("/ws", socketh.socket_app)
|
||||
|
||||
@@ -22,9 +22,9 @@ class Platform(BaseModel):
|
||||
|
||||
@property
|
||||
def rom_count(self) -> int:
|
||||
from handler import dbh
|
||||
from handler import dbplatformh
|
||||
|
||||
return dbh.get_rom_count(self.id)
|
||||
return dbplatformh.get_rom_count(self.id)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
@@ -130,14 +130,14 @@ class Rom(BaseModel):
|
||||
# This is an expensive operation so don't call it on a list of roms
|
||||
@cached_property
|
||||
def sibling_roms(self) -> list["Rom"]:
|
||||
from handler import dbh
|
||||
from handler import dbromh
|
||||
|
||||
if not self.igdb_id:
|
||||
return []
|
||||
|
||||
with dbh.session.begin() as session:
|
||||
with dbromh.session.begin() as session:
|
||||
return session.scalars(
|
||||
dbh.get_roms(self.platform_slug).filter(
|
||||
dbromh.get_roms(platform_id=self.platform_id).filter(
|
||||
Rom.id != self.id,
|
||||
Rom.igdb_id == self.igdb_id,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from ...handler.fs_handler.roms_handler import (
|
||||
from ...handler.fs_handler.fs_roms_handler import (
|
||||
get_rom_cover,
|
||||
get_platforms,
|
||||
get_fs_structure,
|
||||
|
||||
Reference in New Issue
Block a user