Skip to content

Commit

Permalink
Merge pull request #804 from rommapp/feature/add_platform
Browse files Browse the repository at this point in the history
Add platform + general upload roms dialog added
  • Loading branch information
zurdi15 committed Apr 16, 2024
2 parents 6c6ddd7 + 485276d commit 2972e85
Show file tree
Hide file tree
Showing 40 changed files with 1,331 additions and 752 deletions.
46 changes: 42 additions & 4 deletions backend/endpoints/platform.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
from decorators.auth import protected_route
from endpoints.responses import MessageResponse
from endpoints.responses.platform import PlatformSchema
from exceptions.fs_exceptions import PlatformAlreadyExistsException
from fastapi import APIRouter, HTTPException, Request, status
from handler import db_platform_handler
from handler import db_platform_handler, fs_platform_handler
from handler.metadata_handler.igdb_handler import IGDB_PLATFORM_LIST
from handler.scan_handler import scan_platform
from logger.logger import log

router = APIRouter()


@protected_route(router.post, "/platforms", ["platforms.write"])
def add_platforms(request: Request) -> MessageResponse:
async def add_platforms(request: Request) -> PlatformSchema:
"""Create platform endpoint
Args:
request (Request): Fastapi Request object
Returns:
MessageResponse: Standard message response
PlatformSchema: Just created platform
"""

return {"msg": "Enpoint not available yet"}
data = await request.json()
fs_slug = data["fs_slug"]
try:
fs_platform_handler.add_platforms(fs_slug=fs_slug)
except PlatformAlreadyExistsException:
log.info(f"Detected platform: {fs_slug}")
scanned_platform = scan_platform(fs_slug, [fs_slug])
platform = db_platform_handler.add_platform(scanned_platform)
return platform


@protected_route(router.get, "/platforms", ["platforms.read"])
Expand All @@ -37,6 +48,33 @@ def get_platforms(request: Request) -> list[PlatformSchema]:
return db_platform_handler.get_platforms()


@protected_route(router.get, "/platforms/supported", ["platforms.read"])
def get_supported_platforms(request: Request) -> list[PlatformSchema]:
"""Get list of supported platforms endpoint
Args:
request (Request): Fastapi Request object
Returns:
list[PlatformSchema]: List of supported platforms
"""

supported_platforms = []
db_platforms: list = db_platform_handler.get_platforms()
# This double loop probably can be done better
for platform in IGDB_PLATFORM_LIST:
platform["id"] = -1
for p in db_platforms:
if p.name == platform["name"]:
platform["id"] = p.id
platform["fs_slug"] = platform["slug"]
platform["logo_path"] = ""
platform["roms"] = []
platform["rom_count"] = 0
supported_platforms.append(PlatformSchema.model_validate(platform).model_dump())
return supported_platforms


@protected_route(router.get, "/platforms/{id}", ["platforms.read"])
def get_platform(request: Request, id: int) -> PlatformSchema:
"""Get platforms endpoint
Expand Down
4 changes: 2 additions & 2 deletions backend/endpoints/responses/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class PlatformSchema(BaseModel):
igdb_id: Optional[int] = None
sgdb_id: Optional[int] = None
moby_id: Optional[int] = None
name: Optional[str]
logo_path: str
name: str
logo_path: Optional[str] = ""
rom_count: int

class Config:
Expand Down
3 changes: 2 additions & 1 deletion backend/endpoints/responses/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ class SearchRomSchema(BaseModel):
slug: str
name: str
summary: str
url_cover: str
igdb_url_cover: str = ""
moby_url_cover: str = ""
url_screenshots: list[str]
12 changes: 9 additions & 3 deletions backend/endpoints/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ async def update_rom(
request (Request): Fastapi Request object
id (Rom): Rom internal id
rename_as_igdb (bool, optional): Flag to rename rom file as matched IGDB game. Defaults to False.
artwork (Optional[UploadFile], optional): Custom artork to set as cover. Defaults to File(None).
artwork (UploadFile, optional): Custom artork to set as cover. Defaults to File(None).
Raises:
HTTPException: If a rom already have that name when enabling the rename_as_igdb flag
Expand All @@ -274,16 +274,20 @@ async def update_rom(
platform_fs_slug = db_platform_handler.get_platforms(db_rom.platform_id).fs_slug

cleaned_data = {}
cleaned_data["igdb_id"] = data.get("igdb_id", db_rom.igdb_id) or None
cleaned_data["moby_id"] = data.get("moby_id", db_rom.moby_id) or None
cleaned_data["igdb_id"] = data.get("igdb_id", None)
cleaned_data["moby_id"] = data.get("moby_id", None)

if cleaned_data["moby_id"]:
moby_rom = moby_handler.get_rom_by_id(cleaned_data["moby_id"])
cleaned_data.update(moby_rom)
else:
cleaned_data.update({"moby_metadata": {}})

if cleaned_data["igdb_id"]:
igdb_rom = igdb_handler.get_rom_by_id(cleaned_data["igdb_id"])
cleaned_data.update(igdb_rom)
else:
cleaned_data.update({"igdb_metadata": {}})

cleaned_data["name"] = data.get("name", db_rom.name)
cleaned_data["summary"] = data.get("summary", db_rom.summary)
Expand Down Expand Up @@ -325,7 +329,9 @@ async def update_rom(
rom_name=cleaned_data["name"], platform_fs_slug=platform_fs_slug
)
)
cleaned_data.update({"url_cover": ""})
else:
cleaned_data["url_cover"] = data.get("url_cover", db_rom.url_cover)
cleaned_data.update(
fs_resource_handler.get_rom_cover(
overwrite=True,
Expand Down
9 changes: 7 additions & 2 deletions backend/endpoints/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,14 @@ async def search_rom(
search_term, rom.platform.moby_id
)

merged_dict = {item["name"]: item for item in igdb_matched_roms}
merged_dict = {
item["name"]: {**item, "igdb_url_cover": item.pop("url_cover", "")}
for item in igdb_matched_roms
}
for item in moby_matched_roms:
merged_dict[item["name"]] = {
**item,
"moby_url_cover": item.pop("url_cover", ""),
**merged_dict.get(item.get("name", ""), {}),
}

Expand All @@ -86,7 +90,8 @@ async def search_rom(
"slug": "",
"name": "",
"summary": "",
"url_cover": "",
"igdb_url_cover": "",
"moby_url_cover": "",
"url_screenshots": [],
},
**item,
Expand Down
9 changes: 9 additions & 0 deletions backend/exceptions/fs_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ def __repr__(self):
return self.message


class PlatformAlreadyExistsException(Exception):
def __init__(self, fs_slug: str):
self.message = f"Platform {fs_slug} already exists"
super().__init__(self.message)

def __repr__(self):
return self.message


class RomsNotFoundException(Exception):
def __init__(self, platform: str):
self.message = f"Roms not found for platform {platform}. {folder_struct_msg}"
Expand Down
11 changes: 11 additions & 0 deletions backend/handler/fs_handler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import os
import re
from abc import ABC
from enum import Enum
from typing import Final

from config.config_manager import config_manager as cm

DEFAULT_WIDTH_COVER_L: Final = 264 # Width of big cover of IGDB
DEFAULT_HEIGHT_COVER_L: Final = 352 # Height of big cover of IGDB
DEFAULT_WIDTH_COVER_S: Final = 90 # Width of small cover of IGDB
Expand Down Expand Up @@ -84,6 +87,14 @@ class FSHandler(ABC):
def __init__(self) -> None:
pass

def get_fs_structure(self, fs_slug: str) -> str:
cnfg = cm.get_config()
return (
f"{cnfg.ROMS_FOLDER_NAME}/{fs_slug}"
if os.path.exists(cnfg.HIGH_PRIO_STRUCTURE_PATH)
else f"{fs_slug}/{cnfg.ROMS_FOLDER_NAME}"
)

def get_file_name_with_no_extension(self, file_name: str) -> str:
return re.sub(EXTENSION_REGEX, "", file_name).strip()

Expand Down
22 changes: 20 additions & 2 deletions backend/handler/fs_handler/fs_platforms_handler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import os
from pathlib import Path

from config import LIBRARY_BASE_PATH
from config.config_manager import config_manager as cm, Config
from exceptions.fs_exceptions import FolderStructureNotMatchException
from config.config_manager import Config
from config.config_manager import config_manager as cm
from exceptions.fs_exceptions import (
FolderStructureNotMatchException,
PlatformAlreadyExistsException,
)
from handler.fs_handler import FSHandler


Expand All @@ -17,6 +22,19 @@ def _exclude_platforms(self, config: Config, platforms: list):
if platform not in config.EXCLUDED_PLATFORMS
]

def add_platforms(self, fs_slug: str) -> None:
cnfg = cm.get_config()
try:
(
os.mkdir(f"{cnfg.HIGH_PRIO_STRUCTURE_PATH}/{fs_slug}")
if os.path.exists(cnfg.HIGH_PRIO_STRUCTURE_PATH)
else Path(os.path.join(LIBRARY_BASE_PATH, fs_slug, "roms")).mkdir(
parents=True
)
)
except FileExistsError:
raise PlatformAlreadyExistsException(fs_slug)

def get_platforms(self) -> list[str]:
"""Gets all filesystem platforms
Expand Down
8 changes: 0 additions & 8 deletions backend/handler/fs_handler/fs_roms_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ class FSRomsHandler(FSHandler):
def __init__(self) -> None:
pass

def get_fs_structure(self, fs_slug: str):
cnfg = cm.get_config()
return (
f"{cnfg.ROMS_FOLDER_NAME}/{fs_slug}"
if os.path.exists(cnfg.HIGH_PRIO_STRUCTURE_PATH)
else f"{fs_slug}/{cnfg.ROMS_FOLDER_NAME}"
)

def remove_file(self, file_name: str, file_path: str):
try:
os.remove(f"{LIBRARY_BASE_PATH}/{file_path}/{file_name}")
Expand Down
20 changes: 11 additions & 9 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
<script setup lang="ts">
import Notification from "@/components/Notification.vue";
import api from "@/services/api/index";
import storeConfig from "@/stores/config";
import storeHeartbeat from "@/stores/heartbeat";
import socket from "@/services/socket";
import { onBeforeMount } from "vue";
import storeConfig from "@/stores/config";
import storeGalleryFilter from "@/stores/galleryFilter";
import storeHeartbeat from "@/stores/heartbeat";
import storeRoms, { type Rom } from "@/stores/roms";
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, onBeforeUnmount } from "vue";
import { inject, onBeforeMount, onBeforeUnmount } from "vue";
// Props
const scanningStore = storeScanning();
Expand All @@ -23,13 +22,16 @@ const isFiltered = normalizeString(galleryFilter.filterSearch).trim() != "";
const emitter = inject<Emitter<Events>>("emitter");
// Props
const heartbeatStore = storeHeartbeat();
const heartbeat = storeHeartbeat();
const configStore = storeConfig();
socket.on(
"scan:scanning_platform",
({ name, slug, id }: { name: string; slug: string; id: number }) => {
scanningStore.set(true);
scanningPlatforms.value = scanningPlatforms.value.filter(
(platform) => platform.name !== name
);
scanningPlatforms.value.push({ name, slug, id, roms: [] });
}
);
Expand Down Expand Up @@ -94,11 +96,11 @@ onBeforeUnmount(() => {
});
onBeforeMount(() => {
api.get("/heartbeat").then(({ data: heartBeatData }) => {
heartbeatStore.set(heartBeatData);
api.get("/heartbeat").then(({ data: data }) => {
heartbeat.set(data);
});
api.get("/config").then(({ data: configData }) => {
configStore.set(configData);
api.get("/config").then(({ data: data }) => {
configStore.set(data);
});
});
</script>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/__generated__/models/PlatformSchema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/src/__generated__/models/SearchRomSchema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions frontend/src/components/Dashboard/Recent.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<script setup lang="ts">
import LoadingDialog from "@/components/Dialog/Loading.vue";
import DeleteRomDialog from "@/components/Dialog/Rom/DeleteRom.vue";
import EditRomDialog from "@/components/Dialog/Rom/EditRom.vue";
import SearchRomDialog from "@/components/Dialog/Rom/SearchRom.vue";
import UploadRomDialog from "@/components/Dialog/Rom/UploadRom.vue";
import GameCard from "@/components/Game/Card/Base.vue";
import romApi from "@/services/api/rom";
import storeRoms from "@/stores/roms";
Expand Down Expand Up @@ -69,12 +64,6 @@ onMounted(() => {
<!-- TODO: Add a button to upload roms if no roms were uploaded in the last 30 days -->
</v-card-text>
</v-card>

<search-rom-dialog />
<upload-rom-dialog />
<edit-rom-dialog />
<delete-rom-dialog />
<loading-dialog />
</template>
<style scoped>
.scroll {
Expand Down
Loading

0 comments on commit 2972e85

Please sign in to comment.