import json import sys import os import requests import asyncio import aiohttp from uuid_decoder import decode_uuid def parse_spine_material_config(config_json, base_url="", file_name="file"): """ Parse the SpineMaterial config.json file to extract and decode UUID information Args: config_json (str): JSON string containing the SpineMaterial configuration base_url (str): Base URL for generating asset URLs Returns: dict: A dictionary containing the parsed and organized configuration data """ try: config = config_json decoded_data = { "materials": [], "paths": config.get("paths", {}), "types": config.get("types", []), "packs": config.get("packs", {}), "name": config.get("name", ""), "decoded_uuids": [] } uuids = config.get("uuids", []) print(uuids) for uuid in uuids: decoded = decode_uuid(uuid) decoded_data["decoded_uuids"].append({ "original": uuid, "decoded": decoded }) native_map = {} versions_native = config.get("versions", {}).get("native", []) for idx in range(0, len(versions_native), 2): if idx + 1 < len(versions_native): native_map[config["uuids"][versions_native[idx]]] = versions_native[idx + 1] import_map = {} versions_import = config.get("versions", {}).get("import", []) for idx in range(0, len(versions_import), 2): if idx + 1 < len(versions_import): if isinstance(versions_import[idx], int): if versions_import[idx] < len(uuids): import_map[uuids[versions_import[idx]]] = versions_import[idx + 1] else: import_map[versions_import[idx]] = versions_import[idx + 1] decoded_data["native_map"] = native_map decoded_data["import_map"] = import_map for path_index, path_info in config.get("paths", {}).items(): index = int(path_index) material_name = path_info[0] # if 'Avatar' not in material_name : # continue material_type_index = path_info[1] material_type = config.get("types", [])[material_type_index] if material_type_index < len(config.get("types", [])) else "unknown" print(material_type) uuid = uuids[index] if index < len(uuids) else "unknown" decoded_uuid = decode_uuid(uuid) url = "" extension = "unknown" if base_url and uuid in native_map: first_two = decoded_uuid[:2] url = f"{base_url}{config.get('name', '')}/native/{first_two}/{decoded_uuid}.{native_map[uuid]}" if material_type == "sp.SkeletonData": url = f"{base_url}{config.get('name', '')}/native/{first_two}/{decoded_uuid}.{native_map[uuid]}.bin" extension = "bin" elif material_type == "cc.Asset": url = f"{base_url}{config.get('name', '')}/native/{first_two}/{decoded_uuid}.{native_map[uuid]}.atlas" extension = "atlas" elif material_type == "cc.Texture2D": url = f"{base_url}{config.get('name', '')}/native/{first_two}/{decoded_uuid}.{native_map[uuid]}.png" extension = "png" else: continue if not url: continue decoded_data["materials"].append({ "path": material_name, "name": material_name.split("Avatar/")[-1] + "." + extension, "type": material_type, "uuid": uuid, "hash": native_map[uuid], "decoded_uuid": decoded_uuid, "url": url, "extension": extension }) return decoded_data["materials"] except json.JSONDecodeError as e: print(f"Error parsing JSON: {e}") return None except Exception as e: print(f"Error processing config: {e}") return None async def download_file(session, url, target_path): """Download a single file asynchronously""" try: async with session.get(url) as response: if response.status == 200: with open(target_path, 'wb') as f: f.write(await response.read()) print(f"Successfully downloaded {os.path.basename(target_path)}") return True else: print(f"Failed to download {url}, status code: {response.status}") return False except Exception as e: # print(f"Error downloading {url}: {e}") return False async def download_assets_async(): """ Parse JSON files in output/l2d_data directory and download assets to output/downloads using asynchronous requests for improved performance """ input_path = './output/l2d_data/' output_path = './output/downloads/all_files' # Create base output directory if it doesn't exist os.makedirs(output_path, exist_ok=True) download_tasks = [] # Configure connection pool with limits conn = aiohttp.TCPConnector(limit=10) async with aiohttp.ClientSession(connector=conn) as session: # Collect all download tasks for file in os.listdir(input_path): if not file.startswith("Hero_"): continue file_path = os.path.join(input_path, file) try: with open(file_path, 'r') as f: materials = json.load(f) for material in materials: path = material.get("path", "") name = material.get("name", "") url = material.get("url", "") if not url: continue # Create directory based on path target_dir = os.path.join(output_path, path) print(target_dir) os.makedirs(target_dir, exist_ok=True) # Set up the download target_path = os.path.join(target_dir, name) print(f"Queuing download: {url} to {target_path}") # Add download task to the list download_tasks.append(download_file(session, url, target_path)) except Exception as e: print(f"Error processing {file}: {e}") # Execute all download tasks concurrently if download_tasks: print(f"Starting {len(download_tasks)} downloads...") await asyncio.gather(*download_tasks) print("All downloads completed") else: print("No files to download") def download_assets(): """Wrapper to run the async download function""" asyncio.run(download_assets_async()) def main(): base_url = "https://highschool.pro.g123-cpp.com/G123Live/assets/" config_path = './configs/downloads/' output_path = './output/l2d_data/' # Process config files and generate JSON output for file in os.listdir(config_path): if not 'Hero_' in file: continue print(file) # continue file_path = os.path.join(config_path, file) try: with open(f'{file_path}', 'r') as in_file: config_json = json.load(in_file) except Exception as e: print(f"Error reading file: {e}") continue decoded_data = parse_spine_material_config(config_json, base_url, file) if decoded_data: output_file = os.path.join(output_path, file) try: os.makedirs(os.path.dirname(output_file), exist_ok=True) with open(output_file, 'w') as file: json.dump(decoded_data, file, indent=4) print(f"Results written to {output_file}") except Exception as e: print(f"Error writing to {output_file}: {e}") # Download assets from the processed JSON files download_assets() if __name__ == "__main__": main()