diff --git a/backend/endpoints/scan.py b/backend/endpoints/scan.py index 3eb5eb94d..38ae5834b 100644 --- a/backend/endpoints/scan.py +++ b/backend/endpoints/scan.py @@ -1,7 +1,7 @@ -import emoji import json +import emoji -from logger.logger import log, COLORS +from logger.logger import log from utils import fs, fastapi from utils.exceptions import PlatformsNotFoundException, RomsNotFoundException from handler import dbh @@ -9,40 +9,58 @@ from models.platform import Platform from models.rom import Rom -async def scan(_sid: str, platforms: str, complete_rescan: bool=True, sm=None): +async def scan(_sid: str, platforms: str, complete_rescan: bool = True, sm = None): """Scan platforms and roms and write them in database.""" log.info(emoji.emojize(":magnifying_glass_tilted_right: Scanning ")) fs.store_default_resources() - try: # Scanning platforms + try: # Scanning platforms fs_platforms: list[str] = fs.get_platforms() except PlatformsNotFoundException as e: - log.error(e); await sm.emit('scan:done_ko', e.message); return - - platforms: list[str] = json.loads(platforms) if len(json.loads(platforms)) > 0 else fs_platforms + log.error(e) + await sm.emit("scan:done_ko", e.message) + return + + platforms: list[str] = ( + json.loads(platforms) if len(json.loads(platforms)) > 0 else fs_platforms + ) log.info(f"Platforms to be scanned: {', '.join(platforms)}") for platform in platforms: - log.info(emoji.emojize(f":video_game: {platform} {COLORS['reset']}")) + log.info(emoji.emojize(f":video_game: {platform}")) try: scanned_platform: Platform = fastapi.scan_platform(platform) except RomsNotFoundException as e: - log.error(e); continue - await sm.emit('scan:scanning_platform', [scanned_platform.name, scanned_platform.slug]); await sm.emit('') # Workaround to emit in real-time - if platform != str(scanned_platform): log.info(f"Identified as {COLORS['blue']}{scanned_platform}{COLORS['reset']}") + log.error(e) + continue + await sm.emit( + "scan:scanning_platform", [scanned_platform.name, scanned_platform.slug] + ) + await sm.emit("") # Workaround to emit in real-time + if platform != str(scanned_platform): + log.info( + f"Identified as {scanned_platform}" + ) dbh.add_platform(scanned_platform) # Scanning roms fs_roms: list[str] = fs.get_roms(scanned_platform.fs_slug) for rom in fs_roms: - rom_id: int = dbh.rom_exists(scanned_platform.slug, rom['file_name']) - if rom_id and not complete_rescan: continue - await sm.emit('scan:scanning_rom', rom['file_name']); await sm.emit('') # Workaround to emit in real-time - log.info(f"Scanning {COLORS['orange']}{rom['file_name']}{COLORS['reset']}") - if rom['multi']: [log.info(f"\t - {COLORS['orange_i']}{file}{COLORS['reset']}") for file in rom['files']] + rom_id: int = dbh.rom_exists(scanned_platform.slug, rom["file_name"]) + if rom_id and not complete_rescan: + continue + await sm.emit("scan:scanning_rom", rom["file_name"]) + await sm.emit("") # Workaround to emit in real-time + log.info(f"\t - {rom['file_name']}") + if rom["multi"]: + [ + log.info(f"\t\t ยท {file}") + for file in rom["files"] + ] scanned_rom: Rom = fastapi.scan_rom(scanned_platform, rom) - if rom_id: scanned_rom.id = rom_id + if rom_id: + scanned_rom.id = rom_id dbh.add_rom(scanned_rom) - dbh.purge_roms(scanned_platform.slug, [rom['file_name'] for rom in fs_roms]) + dbh.purge_roms(scanned_platform.slug, [rom["file_name"] for rom in fs_roms]) dbh.purge_platforms(fs_platforms) - await sm.emit('scan:done') + await sm.emit("scan:done") diff --git a/backend/endpoints/search.py b/backend/endpoints/search.py index cbb9b0d4e..8a1ecd9e1 100644 --- a/backend/endpoints/search.py +++ b/backend/endpoints/search.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Request import emoji -from logger.logger import log, COLORS +from logger.logger import log from handler import igdbh router = APIRouter() @@ -20,14 +20,14 @@ async def search_rom_igdb( if search_term: log.info(f"Searching by {search_by}: {search_term}") - if search_by == "ID": + if search_by.lower() == "id": matched_roms = igdbh.get_matched_rom_by_id(search_term) - elif search_by == "Name": + elif search_by.lower() == "name": matched_roms = igdbh.get_matched_roms_by_name(search_term, rom["p_igdb_id"]) else: log.info( emoji.emojize( - f":video_game: {rom['p_slug']}: {COLORS['orange']}{rom['file_name']}{COLORS['reset']}" + f":video_game: {rom['p_slug']}: {rom['file_name']}" ) ) matched_roms = igdbh.get_matched_roms( @@ -37,7 +37,7 @@ async def search_rom_igdb( log.info("Results:") [ - log.info(f"\t - {COLORS['blue']}{rom['r_name']}{COLORS['reset']}") + log.info(f"\t - {rom['r_name']}") for rom in matched_roms ] diff --git a/backend/logger/__init__.py b/backend/logger/__init__.py index e69de29bb..2b177c7d2 100644 --- a/backend/logger/__init__.py +++ b/backend/logger/__init__.py @@ -0,0 +1,11 @@ +COLORS: dict = { + 'grey': '\033[92m', + 'pink': '\033[95m', + 'blue': '\033[94m', + 'cyan': '\033[96m', + 'orange': '\033[93m', + 'orange_i': '\033[3;93m', + 'red': '\033[91m', + 'bold_red': '\033[1;91m', + 'reset': '\033[0m', +} \ No newline at end of file diff --git a/backend/logger/file_formatter.py b/backend/logger/file_formatter.py new file mode 100644 index 000000000..36279a375 --- /dev/null +++ b/backend/logger/file_formatter.py @@ -0,0 +1,23 @@ +import logging + +class FileFormatter(logging.Formatter): + + level: str = "%(levelname)s" + dots: str = ":" + identifier: str = "\t [RomM]" + identifier_warning: str = " [RomM]" + identifier_critical: str = " [RomM]" + msg: str = "%(message)s" + date: str = "[%(asctime)s] " + FORMATS: dict = { + logging.DEBUG: level + dots + identifier + date + msg, + logging.INFO: level + dots + identifier + date + msg, + logging.WARNING: level + dots + identifier_warning + date + msg, + logging.ERROR: level + dots + identifier + date + msg, + logging.CRITICAL: level + dots + identifier_critical + date + msg + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(fmt=log_fmt, datefmt='%Y-%m-%d %H:%M:%S') + return formatter.format(record) \ No newline at end of file diff --git a/backend/logger/logger.py b/backend/logger/logger.py index 56fef3002..0b2d59541 100644 --- a/backend/logger/logger.py +++ b/backend/logger/logger.py @@ -1,48 +1,28 @@ import logging +import os +import sys +from datetime import datetime +from pathlib import Path -COLORS: dict = { - 'grey': '\033[92m', - 'pink': '\033[95m', - 'blue': '\033[94m', - 'cyan': '\033[96m', - 'orange': '\033[93m', - 'orange_i': '\033[3;93m', - 'red': '\033[91m', - 'bold_red': '\033[1;91m', - 'reset': '\033[0m', -} - -class CustomFormatter(logging.Formatter): - - level: str = "%(levelname)s" - dots: str = ":" - identifier: str = "\t [RomM]" - identifier_warning: str = " [RomM]" - identifier_critical: str = " [RomM]" - msg: str = "%(message)s" - date: str = "[%(asctime)s] " - FORMATS: dict = { - logging.DEBUG: COLORS['pink'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, - logging.INFO: COLORS['grey'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, - logging.WARNING: COLORS['orange'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier_warning + COLORS['cyan'] + date + COLORS['reset'] + msg, - logging.ERROR: COLORS['red'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, - logging.CRITICAL: COLORS['bold_red'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier_critical + COLORS['cyan'] + date + COLORS['reset'] + msg - } - - def format(self, record): - log_fmt = self.FORMATS.get(record.levelno) - formatter = logging.Formatter(fmt=log_fmt, datefmt='%Y-%m-%d %H:%M:%S') - return formatter.format(record) +from logger.stdout_formatter import StdoutFormatter +from logger.file_formatter import FileFormatter +# Create logs folder if not exists +logs_path = f"{os.getenv('ROMM_BASE_PATH')}/logs" +Path(logs_path).mkdir(parents=True, exist_ok=True) +now = datetime.now() +logs_file = f"{logs_path}/{now.strftime('%Y%m%d_%H%M%S')}.log" +# Get logger log = logging.getLogger("romm") log.setLevel(logging.DEBUG) -# define handler and formatter -handler = logging.StreamHandler() +# Define stdout handler +stdout_handler = logging.StreamHandler(sys.stdout) +stdout_handler.setFormatter(StdoutFormatter()) +log.addHandler(stdout_handler) -# add formatter to handler -handler.setFormatter(CustomFormatter()) - -# add handler to logger -log.addHandler(handler) +# Define file handler +file_handler = logging.FileHandler(logs_file) +file_handler.setFormatter(FileFormatter()) +log.addHandler(file_handler) diff --git a/backend/logger/stdout_formatter.py b/backend/logger/stdout_formatter.py new file mode 100644 index 000000000..7a323bfca --- /dev/null +++ b/backend/logger/stdout_formatter.py @@ -0,0 +1,25 @@ +import logging + +from logger import COLORS + +class StdoutFormatter(logging.Formatter): + + level: str = "%(levelname)s" + dots: str = ":" + identifier: str = "\t [RomM]" + identifier_warning: str = " [RomM]" + identifier_critical: str = " [RomM]" + msg: str = "%(message)s" + date: str = "[%(asctime)s] " + FORMATS: dict = { + logging.DEBUG: COLORS['pink'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, + logging.INFO: COLORS['grey'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, + logging.WARNING: COLORS['orange'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier_warning + COLORS['cyan'] + date + COLORS['reset'] + msg, + logging.ERROR: COLORS['red'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier + COLORS['cyan'] + date + COLORS['reset'] + msg, + logging.CRITICAL: COLORS['bold_red'] + level + COLORS['reset'] + dots + COLORS['blue'] + identifier_critical + COLORS['cyan'] + date + COLORS['reset'] + msg + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(fmt=log_fmt, datefmt='%Y-%m-%d %H:%M:%S') + return formatter.format(record) \ No newline at end of file diff --git a/examples/docker-compose.example.yml b/examples/docker-compose.example.yml index 66c30cc4f..19b4caac2 100644 --- a/examples/docker-compose.example.yml +++ b/examples/docker-compose.example.yml @@ -7,19 +7,20 @@ services: container_name: romm environment: - ROMM_DB_DRIVER=mariadb # This variable can be set as: mariadb | sqlite. If it is not defined, sqlite will be the database by default - - DB_HOST=mariadb # Only needed if ROMM_DB_DRIVER=mariadb - - DB_PORT=3306 # Only needed if ROMM_DB_DRIVER=mariadb - - DB_USER=romm-user # Only needed if ROMM_DB_DRIVER=mariadb - - DB_NAME=romm # Only needed if ROMM_DB_DRIVER=mariadb. Can be optionally changed, and should match the MYSQL_DATABASE value in the mariadb container. - - DB_PASSWD= # Only needed if ROMM_DB_DRIVER=mariadb + - DB_HOST=mariadb # [Optional] Only needed if ROMM_DB_DRIVER=mariadb + - DB_PORT=3306 # [Optional] Only needed if ROMM_DB_DRIVER=mariadb + - DB_USER=romm-user # [Optional] Only needed if ROMM_DB_DRIVER=mariadb + - DB_NAME=romm # [Optional] Only needed if ROMM_DB_DRIVER=mariadb. Can be optionally changed, and should match the MYSQL_DATABASE value in the mariadb container. + - DB_PASSWD= # [Optional] Only needed if ROMM_DB_DRIVER=mariadb - CLIENT_ID= - CLIENT_SECRET= - STEAMGRIDDB_API_KEY= volumes: - '/path/to/library:/romm/library' - - '/path/to/resources:/romm/resources' - - '/path/to/config.yml:/romm/config.yml' - - '/path/to/database:/romm/database' # Only needed if ROMM_DB_DRIVER=sqlite or ROMM_DB_DRIVER not exists + - '/path/to/resources:/romm/resources' # [Optional] Path where roms metadata (cover) are stored + - '/path/to/config.yml:/romm/config.yml' # [Optional] Path where config is stored + - '/path/to/database:/romm/database' # [Optional] Only needed if ROMM_DB_DRIVER=sqlite or ROMM_DB_DRIVER not exists + - '/path/to/logs:/romm/logs' # [Optional] Path where RomM logs are stored ports: - 80:80 depends_on: