Files
romm/backend/endpoints/platform.py
Georges-Antoine Assi 4ed1d82a46 add platform checks
2025-09-15 10:28:32 -04:00

225 lines
7.0 KiB
Python

from datetime import datetime, timezone
from typing import Annotated
from fastapi import Body
from fastapi import Path as PathVar
from fastapi import Request, status
from decorators.auth import protected_route
from endpoints.responses.platform import PlatformSchema
from exceptions.endpoint_exceptions import PlatformNotFoundInDatabaseException
from exceptions.fs_exceptions import PlatformAlreadyExistsException
from handler.auth.constants import Scope
from handler.database import db_platform_handler
from handler.filesystem import fs_platform_handler
from handler.metadata import (
meta_flashpoint_handler,
meta_hasheous_handler,
meta_hltb_handler,
meta_igdb_handler,
meta_launchbox_handler,
meta_moby_handler,
meta_ra_handler,
meta_ss_handler,
meta_tgdb_handler,
)
from handler.metadata.base_handler import UniversalPlatformSlug as UPS
from handler.scan_handler import scan_platform
from logger.formatter import BLUE
from logger.formatter import highlight as hl
from logger.logger import log
from models.platform import DEFAULT_COVER_ASPECT_RATIO, Platform
from utils.router import APIRouter
router = APIRouter(
prefix="/platforms",
tags=["platforms"],
)
@protected_route(
router.post,
"",
[Scope.PLATFORMS_WRITE],
status_code=status.HTTP_201_CREATED,
)
async def add_platform(
request: Request,
fs_slug: Annotated[str, Body(description="Platform slug.", embed=True)],
) -> PlatformSchema:
"""Create a platform."""
try:
await fs_platform_handler.add_platform(fs_slug=fs_slug)
except PlatformAlreadyExistsException:
log.info(f"Detected platform: {hl(fs_slug)}")
scanned_platform = await scan_platform(fs_slug, [fs_slug])
return PlatformSchema.model_validate(
db_platform_handler.add_platform(scanned_platform)
)
@protected_route(router.get, "", [Scope.PLATFORMS_READ])
def get_platforms(request: Request) -> list[PlatformSchema]:
"""Retrieve platforms."""
return [
PlatformSchema.model_validate(p) for p in db_platform_handler.get_platforms()
]
@protected_route(router.get, "/supported", [Scope.PLATFORMS_READ])
def get_supported_platforms(request: Request) -> list[PlatformSchema]:
"""Retrieve the list of supported platforms."""
db_platforms = db_platform_handler.get_platforms()
db_platforms_map = {p.slug: p for p in db_platforms}
now = datetime.now(timezone.utc)
supported_platforms = []
for upslug in UPS:
slug = upslug.value
db_platform = db_platforms_map.get(slug, None)
if db_platform:
supported_platforms.append(
PlatformSchema.model_validate(db_platform).model_dump()
)
continue
igdb_platform = meta_igdb_handler.get_platform(slug)
moby_platform = meta_moby_handler.get_platform(slug)
ss_platform = meta_ss_handler.get_platform(slug)
ra_platform = meta_ra_handler.get_platform(slug)
launchbox_platform = meta_launchbox_handler.get_platform(slug)
hasheous_platform = meta_hasheous_handler.get_platform(slug)
tgdb_platform = meta_tgdb_handler.get_platform(slug)
flashpoint_platform = meta_flashpoint_handler.get_platform(slug)
hltb_platform = meta_hltb_handler.get_platform(slug)
platform_attrs = {
"id": -1,
"name": slug.replace("-", " ").title(),
"fs_slug": slug,
"slug": slug,
"roms": [],
"rom_count": 0,
"created_at": now,
"updated_at": now,
"fs_size_bytes": 0,
"missing_from_fs": False,
"aspect_ratio": DEFAULT_COVER_ASPECT_RATIO,
}
platform_attrs.update(
{
**hltb_platform,
**flashpoint_platform,
**hasheous_platform,
**tgdb_platform,
**launchbox_platform,
**ra_platform,
**moby_platform,
**ss_platform,
**igdb_platform,
"igdb_id": igdb_platform.get("igdb_id")
or hasheous_platform.get("igdb_id")
or None,
"ra_id": ra_platform.get("ra_id")
or hasheous_platform.get("ra_id")
or None,
"tgdb_id": moby_platform.get("tgdb_id")
or hasheous_platform.get("tgdb_id")
or None,
"name": igdb_platform.get("name")
or ss_platform.get("name")
or moby_platform.get("name")
or ra_platform.get("name")
or launchbox_platform.get("name")
or hasheous_platform.get("name")
or tgdb_platform.get("name")
or flashpoint_platform.get("name")
or hltb_platform.get("name")
or slug.replace("-", " ").title(),
"url_logo": igdb_platform.get("url_logo")
or tgdb_platform.get("url_logo")
or "",
}
)
platform = Platform(**platform_attrs)
supported_platforms.append(PlatformSchema.model_validate(platform).model_dump())
return supported_platforms
@protected_route(
router.get,
"/{id}",
[Scope.PLATFORMS_READ],
responses={status.HTTP_404_NOT_FOUND: {}},
)
def get_platform(
request: Request,
id: Annotated[int, PathVar(description="Platform id.", ge=1)],
) -> PlatformSchema:
"""Retrieve a platform by ID."""
platform = db_platform_handler.get_platform(id)
if not platform:
raise PlatformNotFoundInDatabaseException(id)
return PlatformSchema.model_validate(platform)
@protected_route(
router.put,
"/{id}",
[Scope.PLATFORMS_WRITE],
responses={status.HTTP_404_NOT_FOUND: {}},
)
async def update_platform(
request: Request,
id: Annotated[int, PathVar(description="Platform id.", ge=1)],
aspect_ratio: Annotated[str | None, Body(description="Cover aspect ratio.")] = None,
custom_name: Annotated[
str | None, Body(description="Custom platform name.")
] = None,
) -> PlatformSchema:
"""Update a platform."""
platform_db = db_platform_handler.get_platform(id)
if not platform_db:
raise PlatformNotFoundInDatabaseException(id)
if aspect_ratio is not None:
platform_db.aspect_ratio = aspect_ratio
if custom_name is not None:
platform_db.custom_name = custom_name
platform_db = db_platform_handler.add_platform(platform_db)
return PlatformSchema.model_validate(platform_db)
@protected_route(
router.delete,
"/{id}",
[Scope.PLATFORMS_WRITE],
responses={status.HTTP_404_NOT_FOUND: {}},
)
async def delete_platform(
request: Request,
id: Annotated[int, PathVar(description="Platform id.", ge=1)],
) -> None:
"""Delete a platform by ID."""
platform = db_platform_handler.get_platform(id)
if not platform:
raise PlatformNotFoundInDatabaseException(id)
log.info(
f"Deleting {hl(platform.name, color=BLUE)} [{hl(platform.fs_slug)}] from database"
)
db_platform_handler.delete_platform(id)