Created
February 27, 2025 03:15
-
-
Save mstevenson/1d3d712a7e0c90e27c8437e4611b8ec6 to your computer and use it in GitHub Desktop.
Regenerate Unity asset GUIDs while preserving local asset references. Useful for duplicating an entire directory of inter-dependent assets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sys | |
import argparse | |
import uuid | |
import logging | |
from concurrent.futures import ThreadPoolExecutor | |
from typing import List, Tuple, Dict, Optional | |
logging.basicConfig(level=logging.INFO, format='%(message)s') | |
def parse_arguments() -> argparse.Namespace: | |
parser = argparse.ArgumentParser(description='Remap asset references in a directory.') | |
parser.add_argument('--dry-run', action='store_true', help='Perform a dry run without making changes.') | |
parser.add_argument('directory', type=str, help='The directory to process.') | |
return parser.parse_args() | |
def validate_directory(directory: str) -> None: | |
if not os.path.isdir(directory): | |
logging.error(f"Directory {directory} does not exist.") | |
sys.exit(1) | |
def find_files(directory: str, extensions: List[str]) -> List[str]: | |
return [os.path.join(root, file) | |
for root, _, files in os.walk(directory) | |
for file in files if any(file.endswith(ext) for ext in extensions)] | |
def create_replacement_guid_for_meta_file(file_path: str) -> Optional[Tuple[str, str]]: | |
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: | |
for line in f: | |
if line.startswith('guid: '): | |
guid = line.split(' ')[1].strip() | |
new_guid = uuid.uuid4().hex | |
return guid, new_guid | |
return None | |
def replace_guid_in_file(file_path: str, guid_map: Dict[str, str], dry_run: bool) -> None: | |
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: | |
content = f.read() | |
original_content = content | |
for old_guid, new_guid in guid_map.items(): | |
content = content.replace(old_guid, new_guid) | |
if content != original_content: | |
logging.info(f"Updated GUIDs in {file_path}") | |
if not dry_run: | |
with open(file_path, 'w', encoding='utf-8', errors='ignore') as f: | |
f.write(content) | |
def process_meta_files(meta_files: List[str], dry_run: bool) -> Dict[str, str]: | |
guid_map = {} | |
for meta_file in meta_files: | |
result = create_replacement_guid_for_meta_file(meta_file) | |
if result: | |
old_guid, new_guid = result | |
guid_map[old_guid] = new_guid | |
with ThreadPoolExecutor() as executor: | |
futures = [executor.submit(replace_guid_in_file, meta_file, guid_map, dry_run) for meta_file in meta_files] | |
for future in futures: | |
future.result() | |
return guid_map | |
def main() -> None: | |
args = parse_arguments() | |
validate_directory(args.directory) | |
meta_files = find_files(args.directory, ['.meta']) | |
asset_files = find_files(args.directory, ['.prefab', '.mat', '.asset', '.unity']) | |
guid_map = process_meta_files(meta_files, args.dry_run) | |
with ThreadPoolExecutor() as executor: | |
futures = [executor.submit(replace_guid_in_file, file_path, guid_map, args.dry_run) for file_path in asset_files] | |
for future in futures: | |
future.result() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment