Clean asset organization - subfolders + preview versions
This commit is contained in:
115
scripts/add_preview_versions.py
Normal file
115
scripts/add_preview_versions.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Add missing preview versions (256x256) to existing assets.
|
||||
This fixes the problem where you have:
|
||||
- 2 large files (1024x1024) - too big
|
||||
- 2 tiny files (32x32) - can't see them
|
||||
Now adds medium preview versions (256x256) that you CAN see!
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
def add_preview_to_asset(asset_path: Path):
|
||||
"""
|
||||
For each *_styleA.png or *_styleB.png file:
|
||||
- Check if preview version exists
|
||||
- Create 256x256 preview if missing
|
||||
"""
|
||||
|
||||
# Skip if not a style file
|
||||
if not (asset_path.stem.endswith('_styleA') or asset_path.stem.endswith('_styleB')):
|
||||
return False
|
||||
|
||||
# Skip if already a preview or sprite
|
||||
if 'preview' in asset_path.stem or 'sprite' in asset_path.stem or '32x32' in asset_path.stem:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Get base name (remove _styleA or _styleB)
|
||||
stem = asset_path.stem
|
||||
if stem.endswith('_styleA'):
|
||||
base_name = stem[:-7]
|
||||
style = 'styleA'
|
||||
else:
|
||||
base_name = stem[:-7]
|
||||
style = 'styleB'
|
||||
|
||||
# Create preview filename
|
||||
preview_filename = f"{base_name}_{style}_preview_256x256.png"
|
||||
preview_path = asset_path.parent / preview_filename
|
||||
|
||||
# Skip if preview already exists
|
||||
if preview_path.exists():
|
||||
print(f" ⏭️ Preview already exists: {preview_filename}")
|
||||
return False
|
||||
|
||||
# Load original
|
||||
img = Image.open(asset_path)
|
||||
orig_width, orig_height = img.size
|
||||
|
||||
# Create 256x256 preview
|
||||
preview = img.resize((256, 256), Image.Resampling.LANCZOS)
|
||||
preview.save(preview_path, 'PNG', optimize=True)
|
||||
|
||||
print(f" ✅ Created preview: {preview_filename}")
|
||||
print(f" From: {asset_path.name} ({orig_width}×{orig_height})")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error with {asset_path.name}: {e}")
|
||||
return False
|
||||
|
||||
def process_directory(directory: Path):
|
||||
"""Process all PNG files in directory and subdirectories"""
|
||||
|
||||
print(f"\n📂 Processing: {directory}")
|
||||
print("=" * 70)
|
||||
|
||||
# Find all PNG files recursively
|
||||
png_files = list(directory.rglob('*.png'))
|
||||
|
||||
if not png_files:
|
||||
print(" No PNG files found")
|
||||
return 0
|
||||
|
||||
count = 0
|
||||
for png_file in png_files:
|
||||
if add_preview_to_asset(png_file):
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print("🖼️ ADD PREVIEW VERSIONS (256x256) TO EXISTING ASSETS")
|
||||
print("=" * 70)
|
||||
print("\nThis will create visible preview versions of all assets!")
|
||||
print("You'll have:")
|
||||
print(" - assetname_styleA.png (1024×1024 original)")
|
||||
print(" - assetname_styleA_preview_256x256.png (VISIBLE!)")
|
||||
print(" - assetname_styleA_32x32.png (32×32 sprite)")
|
||||
print("=" * 70)
|
||||
|
||||
# Process demo directory
|
||||
demo_dir = Path('assets/images/demo')
|
||||
total = 0
|
||||
|
||||
if demo_dir.exists():
|
||||
total += process_directory(demo_dir)
|
||||
|
||||
# Process other directories
|
||||
images_dir = Path('assets/images')
|
||||
for subdir in images_dir.iterdir():
|
||||
if subdir.is_dir() and subdir.name != 'demo':
|
||||
total += process_directory(subdir)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(f"✅ COMPLETE! Created {total} preview versions")
|
||||
print("=" * 70)
|
||||
print("\n💡 Now your files are VISIBLE in file explorer!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
179
scripts/clean_organize_assets.py
Normal file
179
scripts/clean_organize_assets.py
Normal file
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Clean and organize asset structure:
|
||||
1. Remove duplicate 'originals/' folders
|
||||
2. Organize each asset into its own subfolder:
|
||||
category/
|
||||
assetname/
|
||||
assetname_styleA_1024x1024.png (original)
|
||||
assetname_styleA_preview_256x256.png (visible)
|
||||
assetname_styleA_sprite_32x32.png (game)
|
||||
assetname_styleB_1024x1024.png (original)
|
||||
assetname_styleB_preview_256x256.png (visible)
|
||||
assetname_styleB_sprite_32x32.png (game)
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
|
||||
def get_base_asset_name(filename: str) -> str:
|
||||
"""
|
||||
Extract base asset name from filename.
|
||||
Examples:
|
||||
- campfire_styleA.png -> campfire
|
||||
- campfire_styleA_preview_256x256.png -> campfire
|
||||
- campfire_styleB_32x32.png -> campfire
|
||||
"""
|
||||
stem = filename.replace('.png', '')
|
||||
|
||||
# Remove style suffix
|
||||
if '_styleA' in stem:
|
||||
stem = stem.split('_styleA')[0]
|
||||
elif '_styleB' in stem:
|
||||
stem = stem.split('_styleB')[0]
|
||||
|
||||
return stem
|
||||
|
||||
def get_file_type(filename: str) -> str:
|
||||
"""
|
||||
Determine file type: original, preview, or sprite
|
||||
"""
|
||||
if 'preview' in filename:
|
||||
return 'preview'
|
||||
elif any(size in filename for size in ['32x32', '16x16', '32x64', '24x24']):
|
||||
return 'sprite'
|
||||
else:
|
||||
return 'original'
|
||||
|
||||
def get_style(filename: str) -> str:
|
||||
"""Get style (A or B) from filename"""
|
||||
if 'styleA' in filename:
|
||||
return 'styleA'
|
||||
elif 'styleB' in filename:
|
||||
return 'styleB'
|
||||
return None
|
||||
|
||||
def organize_category(category_path: Path, dry_run: bool = False):
|
||||
"""
|
||||
Organize all assets in a category into subfolders
|
||||
"""
|
||||
|
||||
if not category_path.exists():
|
||||
return
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f"📂 ORGANIZING: {category_path.name}/")
|
||||
print(f"{'='*70}")
|
||||
|
||||
# Delete originals/ folder if it exists (it's duplicates!)
|
||||
originals_path = category_path / 'originals'
|
||||
if originals_path.exists():
|
||||
if not dry_run:
|
||||
shutil.rmtree(originals_path)
|
||||
print(f" 🗑️ Deleted duplicate folder: originals/")
|
||||
|
||||
# Group all PNG files by base asset name
|
||||
asset_groups = defaultdict(list)
|
||||
|
||||
for png_file in category_path.glob('*.png'):
|
||||
if png_file.is_file():
|
||||
base_name = get_base_asset_name(png_file.name)
|
||||
asset_groups[base_name].append(png_file)
|
||||
|
||||
if not asset_groups:
|
||||
print(" No assets to organize")
|
||||
return
|
||||
|
||||
# Organize each asset group into subfolder
|
||||
for asset_name, files in sorted(asset_groups.items()):
|
||||
asset_folder = category_path / asset_name
|
||||
|
||||
if not dry_run:
|
||||
asset_folder.mkdir(exist_ok=True)
|
||||
|
||||
print(f"\n 📁 {asset_name}/")
|
||||
|
||||
for file_path in files:
|
||||
# Rename to proper format if needed
|
||||
file_type = get_file_type(file_path.name)
|
||||
style = get_style(file_path.name)
|
||||
|
||||
if style:
|
||||
# Determine size from filename or type
|
||||
if 'preview' in file_path.name:
|
||||
size_str = '256x256'
|
||||
elif '32x32' in file_path.name:
|
||||
size_str = '32x32'
|
||||
elif '16x16' in file_path.name:
|
||||
size_str = '16x16'
|
||||
elif '32x64' in file_path.name:
|
||||
size_str = '32x64'
|
||||
elif '24x24' in file_path.name:
|
||||
size_str = '24x24'
|
||||
else:
|
||||
# Original - assume 1024x1024
|
||||
size_str = '1024x1024'
|
||||
|
||||
# Create proper filename
|
||||
if file_type == 'preview':
|
||||
new_name = f"{asset_name}_{style}_preview_{size_str}.png"
|
||||
elif file_type == 'sprite':
|
||||
new_name = f"{asset_name}_{style}_sprite_{size_str}.png"
|
||||
else:
|
||||
new_name = f"{asset_name}_{style}_{size_str}.png"
|
||||
|
||||
new_path = asset_folder / new_name
|
||||
|
||||
# Move and rename
|
||||
if not dry_run:
|
||||
file_path.rename(new_path)
|
||||
|
||||
print(f" ✅ {file_type:8} | {style:6} | {new_name}")
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Clean and organize asset structure')
|
||||
parser.add_argument('--dry-run', action='store_true', help='Show what would be done without doing it')
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=" * 70)
|
||||
print("🧹 CLEAN & ORGANIZE ASSETS")
|
||||
print("=" * 70)
|
||||
|
||||
if args.dry_run:
|
||||
print("\n⚠️ DRY RUN MODE - No changes will be made\n")
|
||||
|
||||
print("\nThis will:")
|
||||
print(" 1. Delete 'originals/' folders (duplicates)")
|
||||
print(" 2. Organize each asset into subfolders")
|
||||
print(" 3. Rename files to proper format:")
|
||||
print(" - assetname_styleA_1024x1024.png")
|
||||
print(" - assetname_styleA_preview_256x256.png")
|
||||
print(" - assetname_styleA_sprite_32x32.png")
|
||||
print("=" * 70)
|
||||
|
||||
# Process demo directory
|
||||
demo_dir = Path('assets/images/demo')
|
||||
|
||||
categories = [
|
||||
'terrain', 'crops', 'buildings', 'items',
|
||||
'environment', 'characters', 'enemies',
|
||||
'ui', 'effects', 'npcs'
|
||||
]
|
||||
|
||||
for category in categories:
|
||||
category_path = demo_dir / category
|
||||
organize_category(category_path, dry_run=args.dry_run)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
if not args.dry_run:
|
||||
print("✅ COMPLETE! All assets organized")
|
||||
else:
|
||||
print("✅ DRY RUN COMPLETE! Run without --dry-run to apply changes")
|
||||
print("=" * 70)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -45,10 +45,10 @@ def get_target_size(category: str, filename: str):
|
||||
|
||||
def reorganize_asset(asset_path: Path, category_dir: Path, target_size):
|
||||
"""
|
||||
Reorganize single asset:
|
||||
- Create subfolder with asset name
|
||||
- Save original as assetname_1024x1024.png
|
||||
- Save resized as assetname_32x32.png
|
||||
Reorganize single asset - creates 3 versions:
|
||||
- Original: 1024x1024 (full quality archive)
|
||||
- Preview: 256x256 (visible in file explorer)
|
||||
- Sprite: 32x32 (game-ready tile)
|
||||
"""
|
||||
|
||||
try:
|
||||
@@ -71,20 +71,28 @@ def reorganize_asset(asset_path: Path, category_dir: Path, target_size):
|
||||
print(f" 📁 {asset_name}/")
|
||||
print(f" ✅ Original: {original_filename} ({orig_width}×{orig_height})")
|
||||
|
||||
# Create resized version if needed
|
||||
# Create PREVIEW version (256x256 - visible in file explorer!)
|
||||
preview_size = 256
|
||||
preview = img.resize((preview_size, preview_size), Image.Resampling.LANCZOS)
|
||||
preview_filename = f"{asset_name}_preview_{preview_size}x{preview_size}.png"
|
||||
preview_path = asset_folder / preview_filename
|
||||
preview.save(preview_path, 'PNG', optimize=True)
|
||||
print(f" ✅ Preview: {preview_filename} (VISIBLE SIZE)")
|
||||
|
||||
# Create SPRITE version (32x32 - for game)
|
||||
if target_size is not None:
|
||||
if isinstance(target_size, tuple):
|
||||
resized = img.resize(target_size, Image.Resampling.LANCZOS)
|
||||
sprite = img.resize(target_size, Image.Resampling.LANCZOS)
|
||||
new_width, new_height = target_size
|
||||
else:
|
||||
resized = img.resize((target_size, target_size), Image.Resampling.LANCZOS)
|
||||
sprite = img.resize((target_size, target_size), Image.Resampling.LANCZOS)
|
||||
new_width, new_height = target_size, target_size
|
||||
|
||||
resized_filename = f"{asset_name}_{new_width}x{new_height}.png"
|
||||
resized_path = asset_folder / resized_filename
|
||||
resized.save(resized_path, 'PNG', optimize=True)
|
||||
sprite_filename = f"{asset_name}_sprite_{new_width}x{new_height}.png"
|
||||
sprite_path = asset_folder / sprite_filename
|
||||
sprite.save(sprite_path, 'PNG', optimize=True)
|
||||
|
||||
print(f" ✅ Resized: {resized_filename} ({new_width}×{new_height})")
|
||||
print(f" ✅ Sprite: {sprite_filename} (GAME SIZE)")
|
||||
|
||||
return True
|
||||
|
||||
@@ -139,8 +147,9 @@ def main():
|
||||
print(f"✅ COMPLETE! Reorganized {total} assets")
|
||||
print("=" * 70)
|
||||
print("\n📁 Structure: category/assetname/")
|
||||
print(" - assetname_1024x1024.png (original)")
|
||||
print(" - assetname_32x32.png (resized)")
|
||||
print(" - assetname_1024x1024.png (ORIGINAL - full quality)")
|
||||
print(" - assetname_preview_256x256.png (PREVIEW - visible size)")
|
||||
print(" - assetname_sprite_32x32.png (SPRITE - game ready)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user