import json import requests import os import re import sys from concurrent.futures import ThreadPoolExecutor, as_completed import subprocess import hashlib from vars import home_dir, msg_file from utils import get_settings, log_message settings = get_settings() storage_path = os.path.expandvars(settings["storagePath"]) # Path for the JSON and target folder from command-line arguments save_folder = sys.argv[1] json_path = os.path.join(storage_path, "retrolibrary/cache/missing_artwork_no_hash.json") def rom_parser_ss_get_alias(system): system_map = { "genesis": "1", "ps3": "59", "ngp": "25", "genesiswide": "1", "mastersystem": "2", "nes": "3", "snes": "4", "sneshd": "4", "gb": "9", "gbc": "10", "virtualboy": "11", "gba": "12", "gc": "13", "n64": "14", "nds": "15", "wii": "16", "n3ds": "17", "sega32x": "19", "segacd": "20", "gamegear": "21", "saturn": "22", "dreamcast": "23", "atari2600": "26", "atarijaguar": "27", "atarijaguarcd": "27", "lynx": "28", "3do": "29", "pcengine": "31", "bbcmicro": "37", "atari5200": "40", "atari7800": "41", "atarist": "42", "atari800": "43", "wonderswan": "45", "wonderswancolor": "46", "colecovision": "48", "gw": "52", "psx": "57", "ps2": "58", "psp": "61", "amiga600": "64", "amstradcpc": "65", "c64": "66", "scv": "67", "neogeocd": "70", "pcfx": "72", "vic20": "73", "zxspectrum": "76", "zx81": "77", "x68000": "79", "channelf": "80", "ngpc": "82", "apple2": "86", "gx4000": "87", "dragon": "91", "bk": "93", "vectrex": "102", "supergrafx": "105", "fds": "106", "satellaview": "107", "sufami": "108", "sg1000": "109", "amiga1200": "111", "msx": "113", "pcenginecd": "114", "intellivision": "115", "msx2": "116", "msxturbor": "118", "n64dd": "122", "scummvm": "123", "amigacdtv": "129", "amigacd32": "130", "oricatmos": "131", "amiga": "134", "dos": "135", "prboom": "135", "thomson": "141", "neogeo": "142", "sneswide": "202", "megadrive": "203", "ti994a": "205", "lutro": "206", "supervision": "207", "pc98": "208", "pokemini": "211", "samcoupe": "213", "openbor": "214", "uzebox": "216", "apple2gs": "217", "spectravideo": "218", "palm": "219", "x1": "220", "pc88": "221", "tic80": "222", "solarus": "223", "mame": "230", "easyrpg": "231", "pico8": "234", "pcv2": "237", "pet": "240", "lowresnx": "244", "switch": "225", "wiiU": "18", "primehacks": "16", "naomi": "56", "xbox": "32", "xbox360": "33", "ps4": "60", "doom": "135", "atomiswave": "53" } return system_map.get(system, "unknown") def calculate_md5(filename): hash_md5 = hashlib.md5() with open(filename, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() def fetch_game_artwork(name, platform, media_type): username_ss = "djrodtc" password_ss = "diFay35WElL" api_url_ss = "https://www.screenscraper.fr/api2/" s_id = rom_parser_ss_get_alias(platform) params = { "devid": username_ss, "devpassword": password_ss, "softname": "EmuDeckRetroLibrary", "romnom": name, "systemeid": s_id, "output": "json" } response = requests.get(f"{api_url_ss}jeuInfos.php", params=params) if response.status_code == 200: data = response.json() result = {"name": None, "img": None} if "response" in data and "jeu" in data["response"] and "medias" in data["response"]["jeu"]: medias = data["response"]["jeu"]["medias"] for media in medias: if media.get("type") == "ss" and media_type == 'screenshot': result["name"] = name result["img"] = media["url"] break if media.get("type") == "ss-title" and media_type == 'screenshot': result["name"] = name result["img"] = media["url"] break if media.get("type") == media_type: result["name"] = name result["img"] = media["url"] break if media.get("type") == "box-2D" and media_type == 'box2dfront': result["name"] = name result["img"] = media["url"] break return json.dumps(result) else: return json.dumps({"name": None, "img": None}) def fetch_game_artwork_md5(filename, platform, media_type): username_ss = "djrodtc" password_ss = "diFay35WElL" api_url_ss = "https://www.screenscraper.fr/api2/" s_id = rom_parser_ss_get_alias(platform) rom_md5 = calculate_md5(filename) params = { "devid": username_ss, "devpassword": password_ss, "softname": "EmuDeckRetroLibrary", "rommd5": rom_md5, "systemeid": s_id, "output": "json" } response = requests.get(f"{api_url_ss}jeuInfos.php", params=params) if response.status_code == 200: data = response.json() result = {"name": None, "img": None} if "response" in data and "jeu" in data["response"] and "medias" in data["response"]["jeu"]: medias = data["response"]["jeu"]["medias"] for media in medias: if media.get("type") == "ss" and media_type == 'screenshot': result["name"] = name result["img"] = media["url"] break if media.get("type") == "ss-title" and media_type == 'screenshot': result["name"] = name result["img"] = media["url"] break if media.get("type") == media_type: result["name"] = name result["img"] = media["url"] break if media.get("type") == "box-2D" and media_type == 'box2dfront': result["name"] = name result["img"] = media["url"] break return json.dumps(result) else: return json.dumps({"name": None, "img": None}) def create_empty_image(name, platform, save_folder, type): extension = "jpg" if type != "wheel" else "png" folder_path = os.path.join(save_folder, platform, "media", type) os.makedirs(folder_path, exist_ok=True) img_path = os.path.join(folder_path, f"{name}.{extension}") with open(img_path, 'wb') as file: pass log_message(f"Empty file created: {img_path}") print(f"Empty file created: {img_path}") def download_image(name, platform, img_url, save_folder, type): extension = "jpg" if type != "wheel" else "png" folder_path = os.path.join(save_folder, platform, "media", type) os.makedirs(folder_path, exist_ok=True) img_path = os.path.join(folder_path, f"{name}.{extension}") try: response = requests.get(img_url, timeout=10) response.raise_for_status() # Raise an exception for HTTP error codes with open(img_path, 'wb') as file: file.write(response.content) log_message(f"Image saved: {img_path}") print(f"Image saved: {img_path}") except requests.RequestException as e: log_message(f"Error downloading image for {platform}/{name}: {e}") print(f"Error downloading image for {platform}/{name}: {e}") #create_empty_image(name, platform, save_folder, type) def fetch_image_data(game): name = game['name'] platform = game['platform'] type = game['type'] filename = game['filename'] url = f"https://artwork.emudeck.com/steamdbimg.php?name={name}&platform={platform}&type={type}" try: response = requests.get(url, timeout=10) response.raise_for_status() # Raise an exception for HTTP error codes data = response.json() img_url = data.get('img') if img_url: download_image(name, platform, img_url, save_folder, type) else: #Let's try name matching with SS ss_data = fetch_game_artwork(name, platform, type) ss_data = json.loads(ss_data) if ss_data and ss_data.get('img'): download_image(name, platform, ss_data.get('img'), save_folder, type) else: #Let's try MD5 ss_data = fetch_game_artwork_md5(filename, platform, type) ss_data = json.loads(ss_data) if ss_data and ss_data.get('img'): download_image(name, platform, ss_data.get('img'), save_folder, type) else: print(f"No img found for {name} in {platform}.") #create_empty_image(name, platform, save_folder, type) except requests.RequestException as e: log_message(f"Error processing {platform}/{name}: {e}") print(f"Error processing {platform}/{name}: {e}") #create_empty_image(name, platform, save_folder, type) def process_json(save_folder): # Read the original JSON with open(json_path, 'r') as file: systems = json.load(file) games = [] for system in systems: games.extend(system.get('games', [])) # AƱade todos los juegos del sistema a la lista log_message(f"Starting processing for {len(games)} games...") print(f"Starting processing for {len(games)} games...") # Use ThreadPoolExecutor for parallel downloading with ThreadPoolExecutor(max_workers=5) as executor: futures = [executor.submit(fetch_image_data, game) for game in games] for future in as_completed(futures): try: future.result() # Process exceptions raised in tasks except Exception as e: log_message(f"Error in a task: {e}") print(f"Error in a task: {e}") log_message("Processing completed.") print("Processing completed.") if __name__ == "__main__": log_message("Starting image processing script...") process_json(save_folder) log_message("Image processing script completed.")