feat: update character assets and registry for multiple packs
- Added new character images in .webp format for Dragon Ball FighterZ, Rivals of Aether II, and Skullgirls: 2nd Encore. - Removed outdated .png images for characters in Dragon Ball FighterZ and Rivals of Aether II. - Updated registry.json to reflect changes in character counts and total sizes for packs. - Introduced new packs for BlazBlue Centralfiction and Rivals of Aether II with their respective assets and manifest files. - Added scripts for updating image sizes and renaming images to match character slugs.
This commit is contained in:
+263
@@ -0,0 +1,263 @@
|
||||
# rename_images_to_slug.py
|
||||
#
|
||||
# Renames character images inside a selected pack's /characters folder
|
||||
# to their manifest slug.
|
||||
#
|
||||
# Example:
|
||||
# "Ryu Render.png" -> "ryu.png"
|
||||
#
|
||||
# Matching rules:
|
||||
# - compares normalized filenames against character names
|
||||
# - ignores spaces, punctuation, apostrophes and case
|
||||
# - preserves original extension
|
||||
#
|
||||
# Safe behavior:
|
||||
# - skips already-correct files
|
||||
# - warns about unmatched files
|
||||
# - warns about duplicate matches
|
||||
# - never overwrites existing files
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
REGISTRY_FILENAME = "registry.json"
|
||||
|
||||
IMAGE_EXTENSIONS = (
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".webp",
|
||||
".avif",
|
||||
)
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def normalize(text: str) -> str:
|
||||
"""
|
||||
Normalize text for fuzzy filename matching.
|
||||
|
||||
Example:
|
||||
"Chun-Li" -> "chunli"
|
||||
"Ryu Render" -> "ryurender"
|
||||
"""
|
||||
|
||||
text = text.lower()
|
||||
|
||||
# Remove extension if present
|
||||
text = Path(text).stem
|
||||
|
||||
# Remove non-alphanumeric chars
|
||||
text = re.sub(r"[^a-z0-9]", "", text)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def select_pack(packs: list[dict]) -> Optional[dict]:
|
||||
print("\nAvailable packs:\n")
|
||||
|
||||
for index, pack in enumerate(packs, start=1):
|
||||
print(f"{index}. {pack['name']} ({pack['id']})")
|
||||
|
||||
raw = input("\nSelect pack number: ").strip()
|
||||
|
||||
try:
|
||||
selected_index = int(raw) - 1
|
||||
except ValueError:
|
||||
print("\n❌ Invalid number")
|
||||
return None
|
||||
|
||||
if selected_index < 0 or selected_index >= len(packs):
|
||||
print("\n❌ Invalid selection")
|
||||
return None
|
||||
|
||||
return packs[selected_index]
|
||||
|
||||
|
||||
def build_character_lookup(characters: list[dict]) -> dict[str, str]:
|
||||
"""
|
||||
Build normalized name -> slug mapping.
|
||||
|
||||
Example:
|
||||
"chunli" -> "chun-li"
|
||||
"""
|
||||
|
||||
lookup = {}
|
||||
|
||||
for character in characters:
|
||||
name = character["name"]
|
||||
slug = character["slug"]
|
||||
|
||||
lookup[normalize(name)] = slug
|
||||
|
||||
return lookup
|
||||
|
||||
|
||||
def rename_images(pack_dir: Path) -> None:
|
||||
manifest_path = pack_dir / "manifest.json"
|
||||
characters_dir = pack_dir / "characters"
|
||||
|
||||
if not manifest_path.exists():
|
||||
print("\n❌ manifest.json not found")
|
||||
return
|
||||
|
||||
if not characters_dir.exists():
|
||||
print("\n❌ characters folder not found")
|
||||
return
|
||||
|
||||
manifest = load_json(manifest_path)
|
||||
|
||||
lookup = build_character_lookup(
|
||||
manifest.get("characters", [])
|
||||
)
|
||||
|
||||
image_files = [
|
||||
file
|
||||
for file in characters_dir.iterdir()
|
||||
if file.is_file()
|
||||
and file.suffix.lower() in IMAGE_EXTENSIONS
|
||||
]
|
||||
|
||||
if not image_files:
|
||||
print("\n❌ No images found")
|
||||
return
|
||||
|
||||
print(f'\nScanning "{manifest["name"]}"...\n')
|
||||
|
||||
renamed_count = 0
|
||||
|
||||
for image_file in image_files:
|
||||
normalized_filename = normalize(
|
||||
image_file.name
|
||||
)
|
||||
|
||||
matched_slug = None
|
||||
|
||||
# Exact normalized match
|
||||
if normalized_filename in lookup:
|
||||
matched_slug = lookup[
|
||||
normalized_filename
|
||||
]
|
||||
|
||||
else:
|
||||
# Partial fuzzy fallback
|
||||
matches = [
|
||||
slug
|
||||
for normalized_name, slug in lookup.items()
|
||||
if normalized_name in normalized_filename
|
||||
or normalized_filename in normalized_name
|
||||
]
|
||||
|
||||
if len(matches) == 1:
|
||||
matched_slug = matches[0]
|
||||
|
||||
elif len(matches) > 1:
|
||||
print(
|
||||
f"⚠ Multiple matches for "
|
||||
f"{image_file.name}"
|
||||
)
|
||||
continue
|
||||
|
||||
if matched_slug is None:
|
||||
print(
|
||||
f"⚠ No character match for "
|
||||
f"{image_file.name}"
|
||||
)
|
||||
continue
|
||||
|
||||
new_filename = (
|
||||
matched_slug
|
||||
+ image_file.suffix.lower()
|
||||
)
|
||||
|
||||
new_path = (
|
||||
characters_dir
|
||||
/ new_filename
|
||||
)
|
||||
|
||||
# Already correct
|
||||
if image_file.name.lower() == new_filename.lower():
|
||||
if image_file.name != new_filename:
|
||||
image_file.rename(new_path)
|
||||
print(
|
||||
f"✓ {image_file.name} "
|
||||
f"-> {new_filename}"
|
||||
)
|
||||
renamed_count += 1
|
||||
else:
|
||||
print(f"✓ {image_file.name}")
|
||||
|
||||
continue
|
||||
|
||||
# Prevent overwrite
|
||||
if new_path.exists():
|
||||
print(
|
||||
f"⚠ Target already exists: "
|
||||
f"{new_filename}"
|
||||
)
|
||||
continue
|
||||
|
||||
image_file.rename(new_path)
|
||||
|
||||
renamed_count += 1
|
||||
|
||||
print(
|
||||
f"✓ {image_file.name} "
|
||||
f"-> {new_filename}"
|
||||
)
|
||||
|
||||
print("\n✅ Done!\n")
|
||||
print(f"Renamed: {renamed_count}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
root = Path(__file__).parent.resolve()
|
||||
|
||||
registry_path = (
|
||||
root / REGISTRY_FILENAME
|
||||
)
|
||||
|
||||
if not registry_path.exists():
|
||||
print(
|
||||
"❌ registry.json not found"
|
||||
)
|
||||
return
|
||||
|
||||
registry = load_json(
|
||||
registry_path
|
||||
)
|
||||
|
||||
packs = registry.get(
|
||||
"packs",
|
||||
[],
|
||||
)
|
||||
|
||||
if not packs:
|
||||
print(
|
||||
"❌ No packs found"
|
||||
)
|
||||
return
|
||||
|
||||
selected_pack = select_pack(
|
||||
packs
|
||||
)
|
||||
|
||||
if selected_pack is None:
|
||||
return
|
||||
|
||||
pack_dir = (
|
||||
root
|
||||
/ selected_pack["id"]
|
||||
)
|
||||
|
||||
rename_images(pack_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user