Last active
April 14, 2025 10:32
-
-
Save Neodevils/33f5402889e7c671e80ec00389f9a3ef to your computer and use it in GitHub Desktop.
This script renames SVG file's name to folder's name with splitting some specific pattern. First run rename_sprites.py then run organize_files.py.
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
#!/usr/bin/env python3 | |
import os | |
import re | |
import shutil | |
from pathlib import Path | |
from collections import defaultdict | |
def extract_category(filename): | |
""" | |
Extract the category from a filename. | |
Example: "FaceMad_Lion01.svg" -> "FaceMad" | |
""" | |
# Split by underscore and take the first part | |
parts = filename.split('_', 1) | |
if len(parts) > 1: | |
return parts[0] | |
# If no underscore, try to extract based on camel case or other patterns | |
# This is a fallback for files without underscores | |
match = re.match(r'([A-Za-z]+)', filename) | |
if match: | |
return match.group(1) | |
# If all else fails, return the filename without extension | |
return os.path.splitext(filename)[0] | |
def organize_files(directory_path, output_dir=None, file_types=None, dry_run=False): | |
""" | |
Organize files in the given directory by grouping them into folders based on their name patterns. | |
Args: | |
directory_path: Path to the directory containing files to organize | |
output_dir: Path to output directory (if None, files are organized in place) | |
file_types: List of file extensions to process (e.g., ['.svg', '.png']) | |
dry_run: If True, only print what would be done without actually moving files | |
""" | |
directory_path = Path(directory_path) | |
# If no output directory specified, use the input directory | |
if output_dir is None: | |
output_dir = directory_path | |
else: | |
output_dir = Path(output_dir) | |
output_dir.mkdir(exist_ok=True) | |
# If no file types specified, process all files | |
if file_types is None: | |
file_types = [] # Empty list means all files | |
# Get all files in the directory | |
files = [] | |
for item in directory_path.iterdir(): | |
if item.is_file(): | |
if not file_types or item.suffix.lower() in file_types: | |
files.append(item) | |
print(f"Found {len(files)} files to organize") | |
# Group files by category | |
categories = defaultdict(list) | |
for file_path in files: | |
filename = file_path.name | |
category = extract_category(filename) | |
categories[category].append(file_path) | |
print(f"Grouped files into {len(categories)} categories") | |
# Create folders and move files | |
for category, file_paths in categories.items(): | |
# Only create folders for categories with multiple files | |
if len(file_paths) <= 1: | |
print(f"Skipping category '{category}' with only {len(file_paths)} file") | |
continue | |
# Create category folder | |
category_dir = output_dir / category | |
if not dry_run: | |
category_dir.mkdir(exist_ok=True) | |
print(f"Category '{category}' has {len(file_paths)} files") | |
# Move files to category folder | |
for file_path in file_paths: | |
dest_path = category_dir / file_path.name | |
if dry_run: | |
print(f" Would move {file_path} -> {dest_path}") | |
else: | |
print(f" Moving {file_path.name} -> {category}/{file_path.name}") | |
shutil.copy2(file_path, dest_path) | |
print(f"\nAll done! Files have been organized into categories") | |
def main(): | |
import argparse | |
parser = argparse.ArgumentParser(description='Organize files into folders based on name patterns') | |
parser.add_argument('directory', nargs='?', default='.', help='Directory containing files to organize') | |
parser.add_argument('--output', '-o', help='Output directory (if not specified, files are organized in place)') | |
parser.add_argument('--types', '-t', nargs='+', help='File types to process (e.g., .svg .png)') | |
parser.add_argument('--dry-run', '-d', action='store_true', help='Dry run (don\'t actually move files)') | |
args = parser.parse_args() | |
# Convert file types to lowercase with leading dot | |
file_types = None | |
if args.types: | |
file_types = [f".{t.lower().lstrip('.')}" for t in args.types] | |
organize_files(args.directory, args.output, file_types, args.dry_run) | |
if __name__ == "__main__": | |
main() |
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
#!/bin/bash | |
# Script to organize mob body parts and create JSON configuration | |
# Usage: ./organize_mob_parts.sh <source_directory> <mob_name> | |
# Example: ./organize_mob_parts.sh Needs_Attention/AbominationSpider Abominent_Spider | |
# | |
# Special handling for "_Animation" directory: | |
# - Files in the "_Animation" directory are copied with their original filenames preserved | |
# - These files are included in the JSON configuration under the "_Animation" section | |
# Check if required arguments are provided | |
if [ $# -lt 2 ]; then | |
echo "Usage: $0 <source_directory> <mob_name>" | |
echo "Example: $0 Needs_Attention/AbominationSpider Abominent_Spider" | |
exit 1 | |
fi | |
SOURCE_DIR=$1 | |
MOB_NAME=$2 | |
TARGET_DIR="Sprite/Body_Parts/$MOB_NAME" | |
JSON_DIR="Sprite/Body_Parts/JSON" | |
JSON_FILE="$JSON_DIR/body_$(echo $MOB_NAME | tr '[:upper:]' '[:lower:]').json" | |
# Create target directories | |
mkdir -p "$TARGET_DIR" | |
mkdir -p "$JSON_DIR" | |
echo "Creating directories for $MOB_NAME..." | |
# Find all unique part types (directories) in the source | |
PART_TYPES=$(find "$SOURCE_DIR" -type d -depth 1 | grep -v "_Animation" | sed 's/.*\///' | sort) | |
# Create part type directories | |
for part in $PART_TYPES; do | |
# Skip directories that start with "DefineSprite" | |
if [[ "$part" == DefineSprite* ]]; then | |
continue | |
fi | |
mkdir -p "$TARGET_DIR/$part" | |
echo "Created directory: $TARGET_DIR/$part" | |
done | |
# Find all variants for each part type | |
echo "Identifying variants and copying files..." | |
# Initialize JSON structure | |
JSON_CONTENT="{ | |
\"metadata\": { | |
\"version\": \"1.0\", | |
\"created\": \"$(date +%Y-%m-%d)\", | |
\"description\": \"$MOB_NAME body configurations for different variants\", | |
\"author\": \"BlitzForge Studios\" | |
}, | |
\"variants\": {" | |
# Process each part type | |
VARIANTS=() | |
PART_TYPES_ARRAY=() | |
# First pass: identify all variants | |
for part_dir in "$SOURCE_DIR"/*; do | |
part=$(basename "$part_dir") | |
# Skip directories that start with "DefineSprite" or "_Animation" | |
if [[ "$part" == DefineSprite* ]] || [[ "$part" == _Animation* ]]; then | |
continue | |
fi | |
# Extract variant from part name | |
if [[ "$part" == *_* ]]; then | |
variant=$(echo "$part" | sed 's/.*_//') | |
if [[ ! " ${VARIANTS[@]} " =~ " ${variant} " ]]; then | |
VARIANTS+=("$variant") | |
fi | |
else | |
if [[ ! " ${VARIANTS[@]} " =~ " Default " ]]; then | |
VARIANTS+=("Default") | |
fi | |
fi | |
# Add part to array if not already there | |
base_part=$(echo "$part" | sed 's/_.*$//') | |
if [[ ! " ${PART_TYPES_ARRAY[@]} " =~ " ${base_part} " ]]; then | |
PART_TYPES_ARRAY+=("$base_part") | |
fi | |
done | |
# Add "Default" variant if not already in the list | |
if [[ ! " ${VARIANTS[@]} " =~ " Default " ]]; then | |
VARIANTS=("Default" "${VARIANTS[@]}") | |
fi | |
# Copy files and build JSON | |
for variant in "${VARIANTS[@]}"; do | |
# Start variant section in JSON | |
if [ "$variant" == "Default" ]; then | |
JSON_CONTENT+=" | |
\"Default\": { | |
\"name\": \"Default\", | |
\"description\": \"Default $MOB_NAME body configuration\", | |
\"parts\": {" | |
else | |
JSON_CONTENT+=" | |
\"$variant\": { | |
\"name\": \"$variant\", | |
\"description\": \"$variant variant\", | |
\"parts\": {" | |
fi | |
# Process each part type for this variant | |
for base_part in "${PART_TYPES_ARRAY[@]}"; do | |
# Skip animation sequences (handled separately) | |
if [[ "$base_part" == Swoosh* ]] || [[ "$base_part" == TalonPower* ]] || [[ "$base_part" == Web ]]; then | |
continue | |
fi | |
# Determine source and target paths | |
if [ "$variant" == "Default" ]; then | |
source_file="$SOURCE_DIR/$base_part/$base_part.svg" | |
if [ ! -f "$source_file" ]; then | |
# Try with variant in filename | |
source_file="" | |
continue | |
fi | |
target_file="$TARGET_DIR/$base_part/$variant.svg" | |
else | |
source_file="$SOURCE_DIR/${base_part}_${variant}/${base_part}_${variant}.svg" | |
if [ ! -f "$source_file" ]; then | |
# If variant-specific file doesn't exist, use default | |
source_file="$SOURCE_DIR/$base_part/$base_part.svg" | |
if [ ! -f "$source_file" ]; then | |
# No file found for this part/variant | |
source_file="" | |
continue | |
fi | |
fi | |
target_file="$TARGET_DIR/$base_part/$variant.svg" | |
fi | |
# Copy the file if source exists | |
if [ -n "$source_file" ] && [ -f "$source_file" ]; then | |
cp "$source_file" "$target_file" | |
echo "Copied: $source_file -> $target_file" | |
# Add to JSON | |
JSON_CONTENT+=" | |
\"$base_part\": \"../$MOB_NAME/$base_part/$variant.svg\"," | |
fi | |
done | |
# Remove trailing comma if any parts were added | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
# Close the parts and variant sections | |
JSON_CONTENT+=" | |
} | |
}," | |
done | |
# Remove trailing comma | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
# Close variants section | |
JSON_CONTENT+=" | |
}," | |
# Handle animation sequences | |
echo "Processing animation sequences..." | |
# Initialize animations section | |
JSON_CONTENT+=" | |
\"animations\": {" | |
# Process TalonPowerOn and TalonPowerOff if they exist | |
for anim in TalonPowerOn TalonPowerOff; do | |
if [ -d "$SOURCE_DIR/$anim" ]; then | |
mkdir -p "$TARGET_DIR/$anim/Default" | |
# Check if there are variant-specific animations | |
ANIM_VARIANTS=("Default") | |
for anim_dir in "$SOURCE_DIR/${anim}_"*; do | |
if [ -d "$anim_dir" ]; then | |
variant=$(basename "$anim_dir" | sed "s/${anim}_//") | |
ANIM_VARIANTS+=("$variant") | |
mkdir -p "$TARGET_DIR/$anim/$variant" | |
fi | |
done | |
# Start animation section in JSON | |
JSON_CONTENT+=" | |
\"$anim\": {" | |
# Process each variant | |
for variant in "${ANIM_VARIANTS[@]}"; do | |
if [ "$variant" == "Default" ]; then | |
source_dir="$SOURCE_DIR/$anim" | |
else | |
source_dir="$SOURCE_DIR/${anim}_${variant}" | |
fi | |
# Start variant section in JSON | |
JSON_CONTENT+=" | |
\"$variant\": [" | |
# Copy animation frames | |
frame_count=0 | |
for frame in "$source_dir"/*.svg; do | |
if [ -f "$frame" ]; then | |
frame_num=$(basename "$frame" | sed "s/${anim}_\?${variant}_\?//;s/\.svg//") | |
# Clean up frame number to just be the numeric part | |
clean_num=$(echo "$frame_num" | grep -o '[0-9]\+') | |
target_frame="$TARGET_DIR/$anim/$variant/Frame_$clean_num.svg" | |
mkdir -p "$(dirname "$target_frame")" | |
cp "$frame" "$target_frame" | |
echo "Copied animation frame: $frame -> $target_frame" | |
# Add to JSON | |
JSON_CONTENT+=" | |
\"../$MOB_NAME/$anim/$variant/Frame_$clean_num.svg\"," | |
((frame_count++)) | |
fi | |
done | |
# Remove trailing comma if frames were added | |
if [ $frame_count -gt 0 ]; then | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
fi | |
# Close variant section | |
JSON_CONTENT+=" | |
]," | |
done | |
# Remove trailing comma | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
# Close animation section | |
JSON_CONTENT+=" | |
}," | |
fi | |
done | |
# Remove trailing comma if any animations were added | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
# Close animations section | |
JSON_CONTENT+=" | |
}," | |
# Handle effects (Swoosh, Web, etc.) | |
echo "Processing effects..." | |
# Initialize effects section | |
JSON_CONTENT+=" | |
\"effects\": {" | |
# Process Swoosh effects | |
for effect in Swoosh01 Swoosh02 Swoosh03 Swoosh04 Web; do | |
if [ -d "$SOURCE_DIR/$effect" ]; then | |
mkdir -p "$TARGET_DIR/$effect" | |
# Start effect section in JSON | |
JSON_CONTENT+=" | |
\"$effect\": [" | |
# Copy effect frames | |
frame_count=0 | |
for frame in "$SOURCE_DIR/$effect"/*.svg; do | |
if [ -f "$frame" ]; then | |
frame_num=$(basename "$frame" | sed "s/${effect}_//;s/\.svg//") | |
# Clean up frame number to just be the numeric part | |
clean_num=$(echo "$frame_num" | grep -o '[0-9]\+') | |
target_frame="$TARGET_DIR/$effect/Frame_$clean_num.svg" | |
mkdir -p "$(dirname "$target_frame")" | |
cp "$frame" "$target_frame" | |
echo "Copied effect frame: $frame -> $target_frame" | |
# Add to JSON | |
JSON_CONTENT+=" | |
\"../$MOB_NAME/$effect/Frame_$clean_num.svg\"," | |
((frame_count++)) | |
fi | |
done | |
# Remove trailing comma if frames were added | |
if [ $frame_count -gt 0 ]; then | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
fi | |
# Close effect section | |
JSON_CONTENT+=" | |
]," | |
fi | |
done | |
# Remove trailing comma if any effects were added | |
JSON_CONTENT="${JSON_CONTENT%,}" | |
# Close effects section | |
JSON_CONTENT+=" | |
}," | |
# Handle _Animation directory if it exists | |
echo "Processing _Animation directory..." | |
if [ -d "$SOURCE_DIR/_Animation" ]; then | |
# Create target directory for animations | |
mkdir -p "$TARGET_DIR/_Animation" | |
# Initialize animations section in JSON | |
JSON_CONTENT+=" | |
\"_Animation\": {" | |
# Find all animation files | |
animation_files=("$SOURCE_DIR/_Animation"/*.svg) | |
if [ ${#animation_files[@]} -gt 0 ] && [ -f "${animation_files[0]}" ]; then | |
# Copy all animation files with their original names | |
for anim_file in "$SOURCE_DIR/_Animation"/*.svg; do | |
if [ -f "$anim_file" ]; then | |
# Get the original filename without path | |
orig_filename=$(basename "$anim_file") | |
# Copy the file with its original name | |
cp "$anim_file" "$TARGET_DIR/_Animation/$orig_filename" | |
echo "Copied animation file (preserving name): $anim_file -> $TARGET_DIR/_Animation/$orig_filename" | |
fi | |
done | |
# Add animation files to JSON with their original names | |
JSON_CONTENT+=" | |
\"files\": [" | |
# Add each animation file to JSON | |
first_file=true | |
for anim_file in "$SOURCE_DIR/_Animation"/*.svg; do | |
if [ -f "$anim_file" ]; then | |
orig_filename=$(basename "$anim_file") | |
# Add comma before all but the first item | |
if [ "$first_file" = true ]; then | |
first_file=false | |
else | |
JSON_CONTENT+="," | |
fi | |
# Add file path to JSON | |
JSON_CONTENT+=" | |
\"../$MOB_NAME/_Animation/$orig_filename\"" | |
fi | |
done | |
# Close files array | |
JSON_CONTENT+=" | |
]" | |
fi | |
# Close _Animation section | |
JSON_CONTENT+=" | |
}" | |
fi | |
# Close the JSON structure | |
JSON_CONTENT+=" | |
}" | |
# Write JSON to file | |
echo "$JSON_CONTENT" > "$JSON_FILE" | |
echo "Created JSON configuration: $JSON_FILE" | |
echo "Organization complete for $MOB_NAME!" |
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 re | |
import shutil | |
from pathlib import Path | |
def extract_proper_name(folder_name): | |
"""Extract the proper name from the folder name. | |
Example: "DefineSprite_2_a_WaistSide_ThunderGolem" -> "WaistSide_ThunderGolem" | |
""" | |
print(f" Extracting name from: {folder_name}") | |
# Pattern to match: after "a_" and capture everything until the end | |
match = re.search(r'a_(.+)$', folder_name) | |
if match: | |
result = match.group(1) | |
print(f" Found a_ pattern: {result}") | |
return result | |
# For folders without 'a_' pattern but with underscores, extract the meaningful part | |
# Example: "DefineSprite_1234_Torso_EscapedGladiator" -> "Torso_EscapedGladiator" | |
match = re.search(r'DefineSprite_(\d+)_(.+)$', folder_name) | |
if match: | |
result = match.group(2) | |
print(f" Found DefineSprite pattern: {result}") | |
return result | |
print(f" No pattern matched, using original: {folder_name}") | |
return folder_name # Fallback to original name if pattern not found | |
def main(): | |
# Create output directory | |
output_dir = Path("sprites_done") | |
output_dir.mkdir(exist_ok=True) | |
# Get all directories in the current folder | |
directories = [d for d in os.listdir() if os.path.isdir(d) and not d.startswith("sprites_done")] | |
print(f"Found {len(directories)} directories to process") | |
# Process each directory | |
for folder in directories: | |
# Extract proper name from folder | |
proper_name = extract_proper_name(folder) | |
# Get all SVG files in the folder | |
svg_files = [f for f in os.listdir(folder) if f.lower().endswith('.svg')] | |
num_files = len(svg_files) | |
print(f"Processing {folder} -> {proper_name} ({num_files} SVG files)") | |
# If multiple SVG files, create a subfolder | |
subfolder = None | |
if num_files > 1: | |
subfolder = output_dir / proper_name | |
subfolder.mkdir(exist_ok=True) | |
# Process each SVG file | |
for i, svg_file in enumerate(sorted(svg_files), 1): | |
source_path = Path(folder) / svg_file | |
# Determine new filename | |
if num_files == 1: | |
new_filename = f"{proper_name}.svg" | |
dest_path = output_dir / new_filename | |
else: | |
new_filename = f"{proper_name}_{i:02d}.svg" | |
dest_path = subfolder / new_filename | |
# Copy the file | |
shutil.copy2(source_path, dest_path) | |
print(f" Copied {svg_file} -> {new_filename}") | |
print(f"\nAll done! Files have been saved to {output_dir}") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment