updated README
2
.gitignore
vendored
@@ -40,4 +40,4 @@ envs.env
|
||||
mariadb
|
||||
|
||||
# data test
|
||||
emulation
|
||||
library
|
||||
89
README-DEV.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# romm
|
||||
|
||||
## Project Setup for development
|
||||
|
||||
### Create python virtualenv
|
||||
|
||||
```sh
|
||||
python3 -m venv backend/venv/romm
|
||||
```
|
||||
|
||||
### Activate romm virtualenv
|
||||
|
||||
```sh
|
||||
source backend/venv/romm/bin/activate
|
||||
```
|
||||
|
||||
### Install venv dependencies
|
||||
|
||||
```sh
|
||||
pip install -r backend/dependencies/requirements.txt
|
||||
```
|
||||
|
||||
### Create environment variables file with the following variables
|
||||
|
||||
```sh
|
||||
touch envs.env
|
||||
|
||||
# IGDB auth
|
||||
CLIENT_ID=""
|
||||
CLIENT_SECRET=""
|
||||
|
||||
# STEAMGRIDDB API KEY
|
||||
STEAMGRIDDB_API_KEY=""
|
||||
|
||||
# Library path
|
||||
LIBRARY_PATH="library_path/"
|
||||
|
||||
# DB related config
|
||||
ROMM_DB_HOST=""
|
||||
ROMM_DB_PORT=
|
||||
ROMM_DB_ROOT_PASSWD=""
|
||||
ROMM_DB_USER=""
|
||||
ROMM_DB_PASSWD=""
|
||||
ROMM_DB_CONFIG_PATH=""
|
||||
```
|
||||
|
||||
### Export environment variables
|
||||
|
||||
```sh
|
||||
export $(cat envs.env | xargs)
|
||||
```
|
||||
|
||||
### Create mariadb docker container
|
||||
|
||||
```sh
|
||||
docker-compose up -d (to your docker-compose file for run mariadb)
|
||||
```
|
||||
|
||||
### Install node.js dependencies
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
## Project Testing
|
||||
|
||||
### Activate romm virtualenv
|
||||
|
||||
```sh
|
||||
source backend/venv/romm/bin/activate
|
||||
```
|
||||
|
||||
### Export environment variables
|
||||
|
||||
```sh
|
||||
export $(cat envs.env | xargs)
|
||||
```
|
||||
|
||||
### Start backend API
|
||||
|
||||
```sh
|
||||
python3 backend/src/main.py
|
||||
```
|
||||
|
||||
### Start frontend (compile and hot-reload)
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
125
README.md
@@ -1,93 +1,68 @@
|
||||
# romm
|
||||
<div align="center">
|
||||
<img src="romm.svg" height="128px" width="auto" alt="Gameyfin Logo">
|
||||
<h1 style="padding:20px;">RomM (Rom Manager)</h1>
|
||||
<br/><br/>
|
||||
</div>
|
||||
|
||||
## Docker image
|
||||
# Overview
|
||||
|
||||
[romm](https://hub.docker.com/r/zurdi15/romm)
|
||||
Inspired by [Jellyfin](https://jellyfin.org/) and after found that the awesome [Gameyfin](https://github.com/grimsi/gameyfin) project is not supported for arm64 architectures (since my own homelab is only made by 3 rpis) and it is a general game library manager, I decided to develop my own game library solution, focused on retro gaming.
|
||||
|
||||
## Project Setup for development
|
||||
For now, it is only available as a docker [image](https://hub.docker.com/r/zurdi15/romm) (amd64/arm64)
|
||||
|
||||
### Create python virtualenv
|
||||
## Features
|
||||
|
||||
```sh
|
||||
python3 -m venv backend/venv/romm
|
||||
* Scans your game library (all at once or by platform) and enriches it with IGDB metadata
|
||||
* Access your library via your web-browser
|
||||
* Possibility to select one of the matching IGDB results if the scan doesn't get the right one
|
||||
* Download games directly from your web-browser
|
||||
* Edit your game files directly from your web-browser
|
||||
* Set a custom cover for each game [WIP]
|
||||
* Upload games directly from your web-browser [WIP]
|
||||
* Manage save files directly from your web-browser [WIP]
|
||||
* Responsive design
|
||||
* Light and dark theme
|
||||
|
||||
# Prerequisites
|
||||
|
||||
To allow RomM scan your retro games library, it should follow the following structure:
|
||||
|
||||
```
|
||||
library/
|
||||
├─ gbc/
|
||||
│ ├─ roms/
|
||||
│ │ ├─ rom_1.gbc
|
||||
│ │ ├─ rom_2.gbc
|
||||
|
|
||||
├─ gba/
|
||||
│ ├─ roms/
|
||||
│ │ ├─ rom_1.gba
|
||||
│ │ ├─ rom_2.gba
|
||||
|
|
||||
├─ gb/
|
||||
│ ├─ roms/
|
||||
│ │ ├─ rom_1.gb
|
||||
│ │ ├─ rom_1.gb
|
||||
```
|
||||
|
||||
### Activate romm virtualenv
|
||||
# Preview
|
||||
|
||||
```sh
|
||||
source backend/venv/romm/bin/activate
|
||||
```
|
||||
## Desktop
|
||||
|
||||
### Install venv dependencies
|
||||
|
||||
```sh
|
||||
pip install -r backend/dependencies/requirements.txt
|
||||
```
|
||||
|
||||
### Create environment variables file with the following variables
|
||||
## Mobile
|
||||
|
||||
```sh
|
||||
touch backend/envs.env
|
||||
[WIP]
|
||||
|
||||
# IGDB auth
|
||||
CLIENT_ID=""
|
||||
CLIENT_SECRET=""
|
||||
# Docker image
|
||||
|
||||
# STEAMGRIDDB API KEY
|
||||
STEAMGRIDDB_API_KEY=""
|
||||
Last version of the docker [image](https://hub.docker.com/r/zurdi15/romm/tags)
|
||||
|
||||
# Platforms system path
|
||||
PLATFORMS_SYSTEM_BASE_PATH="emulation"
|
||||
## Installation
|
||||
|
||||
# DB related config
|
||||
ROMM_DB_HOST=""
|
||||
ROMM_DB_PORT=
|
||||
ROMM_DB_ROOT_PASSWD=""
|
||||
ROMM_DB_USER=""
|
||||
ROMM_DB_PASSWD=""
|
||||
ROMM_DB_CONFIG_PATH=""
|
||||
```
|
||||
Check the [docker-compose.yml](https://github.com/zurdi15/romm/blob/master/docker/docker-compose.example.yml) example
|
||||
|
||||
### Export environment variables
|
||||
## Troubleshoot
|
||||
|
||||
```sh
|
||||
export $(cat backend/envs.env | xargs)
|
||||
```
|
||||
|
||||
### Create mariadb docker container
|
||||
|
||||
```sh
|
||||
docker-compose up -d (to your docker-compose file for run mariadb)
|
||||
```
|
||||
|
||||
### Install node.js dependencies
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
## Project Testing
|
||||
|
||||
### Activate romm virtualenv
|
||||
|
||||
```sh
|
||||
source backend/venv/romm/bin/activate
|
||||
```
|
||||
|
||||
### Export environment variables
|
||||
|
||||
```sh
|
||||
export $(cat backend/envs.env | xargs)
|
||||
```
|
||||
|
||||
### Start backend API
|
||||
|
||||
```sh
|
||||
python3 backend/src/main.py
|
||||
```
|
||||
|
||||
### Start frontend (compile and hot-reload)
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
After the first installation, sometimes the RomM container can have problems connecting with the database. Restarting the RomM container may solve the problem.
|
||||
|
||||
@@ -6,15 +6,15 @@ DEV_PORT: int = 5000
|
||||
DEV_HOST: str = "0.0.0.0"
|
||||
|
||||
# PATHS
|
||||
EMULATION_BASE_PATH: str = f"{pathlib.Path(__file__).parent.parent.parent.parent.resolve()}/emulation"
|
||||
LIBRARY_BASE_PATH: str = f"{pathlib.Path(__file__).parent.parent.parent.parent.resolve()}/library"
|
||||
|
||||
DEFAULT_URL_LOGO: str = "https://images.igdb.com/igdb/image/upload/t_cover_big/nocover.png"
|
||||
DEFAULT_PATH_LOGO: str = f"/assets/emulation/resources/default/logo_l.png"
|
||||
DEFAULT_PATH_LOGO: str = f"/assets/library/resources/default/logo_l.png"
|
||||
|
||||
DEFAULT_URL_COVER_L: str = "https://images.igdb.com/igdb/image/upload/t_cover_big/nocover.png"
|
||||
DEFAULT_PATH_COVER_L: str = f"/assets/emulation/resources/default/cover_l.png"
|
||||
DEFAULT_PATH_COVER_L: str = f"/assets/library/resources/default/cover_l.png"
|
||||
DEFAULT_URL_COVER_S: str = "https://images.igdb.com/igdb/image/upload/t_cover_small/nocover.png"
|
||||
DEFAULT_PATH_COVER_S: str = f"/assets/emulation/resources/default/cover_s.png"
|
||||
DEFAULT_PATH_COVER_S: str = f"/assets/library/resources/default/cover_s.png"
|
||||
|
||||
|
||||
# IGDB
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../emulation
|
||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import requests
|
||||
from fastapi import HTTPException
|
||||
|
||||
from config.config import EMULATION_BASE_PATH, DEFAULT_URL_LOGO, DEFAULT_URL_COVER_L, DEFAULT_PATH_COVER_L, DEFAULT_URL_COVER_S, DEFAULT_PATH_COVER_S
|
||||
from config.config import LIBRARY_BASE_PATH, DEFAULT_URL_LOGO, DEFAULT_URL_COVER_L, DEFAULT_PATH_COVER_L, DEFAULT_URL_COVER_S, DEFAULT_PATH_COVER_S
|
||||
from logger.logger import log
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ def p_logo_exists(slug: str) -> bool:
|
||||
Returns
|
||||
True if logo exists in filesystem else False
|
||||
"""
|
||||
logo_path: str = f"{EMULATION_BASE_PATH}/resources/{slug}/logo.png"
|
||||
logo_path: str = f"{LIBRARY_BASE_PATH}/resources/{slug}/logo.png"
|
||||
return True if os.path.exists(logo_path) else False
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ def get_p_path_logo(slug: str) -> str:
|
||||
Args:
|
||||
slug: shor name of the platform
|
||||
"""
|
||||
return f"/assets/emulation/resources/{slug}/logo.png"
|
||||
return f"/assets/library/resources/{slug}/logo.png"
|
||||
|
||||
|
||||
def store_p_logo(slug: str, url_logo: str) -> None:
|
||||
@@ -54,7 +54,7 @@ def store_p_logo(slug: str, url_logo: str) -> None:
|
||||
url_logo: url to get logo
|
||||
"""
|
||||
logo_file: str = f"logo.png"
|
||||
logo_path: str = f"{EMULATION_BASE_PATH}/resources/{slug}"
|
||||
logo_path: str = f"{LIBRARY_BASE_PATH}/resources/{slug}"
|
||||
res = requests.get(url_logo, stream=True)
|
||||
if res.status_code == 200:
|
||||
Path(logo_path).mkdir(parents=True, exist_ok=True)
|
||||
@@ -68,11 +68,11 @@ def store_p_logo(slug: str, url_logo: str) -> None:
|
||||
def get_platforms() -> list:
|
||||
"""Gets all filesystem platforms
|
||||
|
||||
Returns list with all the filesystem platforms found in the EMULATION_BASE_PATH.
|
||||
Returns list with all the filesystem platforms found in the LIBRARY_BASE_PATH.
|
||||
Automatically discards the default directory.
|
||||
"""
|
||||
try:
|
||||
platforms: list[str] = list(os.walk(EMULATION_BASE_PATH))[0][1]
|
||||
platforms: list[str] = list(os.walk(LIBRARY_BASE_PATH))[0][1]
|
||||
if 'resources' in platforms: platforms.remove('resources')
|
||||
log.info(f"filesystem platforms found: {platforms}")
|
||||
return platforms
|
||||
@@ -91,7 +91,7 @@ def r_cover_exists(p_slug: str, filename_no_ext: str, size: str) -> bool:
|
||||
Returns
|
||||
True if cover exists in filesystem else False
|
||||
"""
|
||||
logo_path: str = f"{EMULATION_BASE_PATH}/resources/{p_slug}/{filename_no_ext}_{size}.png"
|
||||
logo_path: str = f"{LIBRARY_BASE_PATH}/resources/{p_slug}/{filename_no_ext}_{size}.png"
|
||||
return True if os.path.exists(logo_path) else False
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ def get_r_cover_path(p_slug: str, filename_no_ext: str, size: str) -> str:
|
||||
filename_no_ext: name of rom file without extension
|
||||
size: size of the cover -> big as 'l' | small as 's'
|
||||
"""
|
||||
return f"/assets/emulation/resources/{p_slug}/{filename_no_ext}_{size}.png"
|
||||
return f"/assets/library/resources/{p_slug}/{filename_no_ext}_{size}.png"
|
||||
|
||||
|
||||
def store_r_cover(p_slug: str, filename_no_ext: str, url_cover: str, size: str) -> None:
|
||||
@@ -116,7 +116,7 @@ def store_r_cover(p_slug: str, filename_no_ext: str, url_cover: str, size: str)
|
||||
size: size of the cover -> big as 'l' | small as 's'
|
||||
"""
|
||||
cover_file: str = f"{filename_no_ext}_{size}.png"
|
||||
cover_path: str = f"{EMULATION_BASE_PATH}/resources/{p_slug}/"
|
||||
cover_path: str = f"{LIBRARY_BASE_PATH}/resources/{p_slug}/"
|
||||
sizes: dict = {'l': 'big', 's': 'small'}
|
||||
res = requests.get(url_cover.replace('t_thumb', f't_cover_{sizes[size]}'), stream=True)
|
||||
if res.status_code == 200:
|
||||
@@ -150,14 +150,14 @@ def get_roms(p_slug: str) -> list:
|
||||
|
||||
Args:
|
||||
p_slug: short name of the platform
|
||||
Returns: list with all the filesystem roms for a platform found in the EMULATION_BASE_PATH.
|
||||
Returns: list with all the filesystem roms for a platform found in the LIBRARY_BASE_PATH.
|
||||
Automatically discards the default directory.
|
||||
"""
|
||||
try:
|
||||
roms_filename = list(os.walk(f"{EMULATION_BASE_PATH}/{p_slug}/roms/"))[0][2]
|
||||
roms_filename = list(os.walk(f"{LIBRARY_BASE_PATH}/{p_slug}/roms/"))[0][2]
|
||||
roms: list[dict] = [
|
||||
{'filename': rom,
|
||||
'size': str(round(os.stat(f"{EMULATION_BASE_PATH}/{p_slug}/roms/{rom}").st_size / (1024 * 1024), 2))}
|
||||
'size': str(round(os.stat(f"{LIBRARY_BASE_PATH}/{p_slug}/roms/{rom}").st_size / (1024 * 1024), 2))}
|
||||
for rom in roms_filename]
|
||||
log.info(f"filesystem roms found for {p_slug}: {roms}")
|
||||
except IndexError:
|
||||
@@ -175,7 +175,7 @@ def r_exists(p_slug: str, filename: str) -> bool:
|
||||
Returns
|
||||
True if rom exists in filesystem else False
|
||||
"""
|
||||
rom_path: str = f"{EMULATION_BASE_PATH}/{p_slug}/roms/{filename}"
|
||||
rom_path: str = f"{LIBRARY_BASE_PATH}/{p_slug}/roms/{filename}"
|
||||
exists: bool = True if os.path.exists(rom_path) else False
|
||||
return exists
|
||||
|
||||
@@ -183,12 +183,12 @@ def r_exists(p_slug: str, filename: str) -> bool:
|
||||
def rename_rom(p_slug: str, filename: str, data: dict) -> None:
|
||||
if data['filename'] != filename:
|
||||
if r_exists(p_slug, data['filename']): raise HTTPException(status_code=500, detail=f"Can't rename: {data['filename']} already exists.")
|
||||
os.rename(f"{EMULATION_BASE_PATH}/{p_slug}/roms/{filename}",
|
||||
f"{EMULATION_BASE_PATH}/{p_slug}/roms/{data['filename']}")
|
||||
os.rename(f"{LIBRARY_BASE_PATH}/{p_slug}/roms/{filename}",
|
||||
f"{LIBRARY_BASE_PATH}/{p_slug}/roms/{data['filename']}")
|
||||
|
||||
|
||||
def delete_rom(p_slug: str, filename: str) -> None:
|
||||
try:
|
||||
os.remove(f"{EMULATION_BASE_PATH}/{p_slug}/roms/{filename}")
|
||||
os.remove(f"{LIBRARY_BASE_PATH}/{p_slug}/roms/{filename}")
|
||||
except FileNotFoundError:
|
||||
log.warning(f"Rom not found in filesystem: {p_slug}/{filename}")
|
||||
|
||||
@@ -7,5 +7,5 @@ venv/
|
||||
**/__pycache__
|
||||
__pycache__/
|
||||
|
||||
**/assets/emulation
|
||||
../frontend/assets/emulation/
|
||||
**/assets/library
|
||||
../frontend/assets/library/
|
||||
@@ -10,7 +10,7 @@ RUN npm run build
|
||||
FROM ubuntu/nginx:1.18-22.04_edge as production-stage
|
||||
COPY --from=front-build-stage /front/dist /var/www/html
|
||||
COPY ./frontend/assets/platforms /var/www/html/assets/platforms
|
||||
RUN ln -s /emulation /var/www/html/assets/emulation
|
||||
RUN ln -s /library /var/www/html/assets/library
|
||||
|
||||
# setup backend
|
||||
RUN apt update
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- CLIENT_SECRET=${CLIENT_SECRET}
|
||||
- STEAMGRIDDB_API_KEY=WIP
|
||||
volumes:
|
||||
- ${EMULATION_PATH}:/emulation'
|
||||
- ${LIBRARY_PATH}:/library'
|
||||
ports:
|
||||
- '${ROMM_PORT}:80'
|
||||
restart: "no"
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |