#!/usr/bin/env python3
"""Rename images or collections and keep everything in sync.
Usage:
python3 rename.py image <collection> <old-name> <new-name>
python3 rename.py collection <old-name> <new-name>
python3 rename.py list [collection]
Examples:
python3 rename.py list
python3 rename.py list in-dreams
python3 rename.py image in-dreams "1009494620_364f90e07e_o" "tangled-roots"
python3 rename.py collection in-dreams "dreams"
Names are without extensions -- the script handles .jpg and .md automatically.
"""
import json
import os
import sys
BASE = os.path.dirname(os.path.abspath(__file__))
IMAGES_DIR = os.path.join(BASE, "images")
CONFIG_PATH = os.path.join(BASE, "site.json")
def load_config():
if os.path.exists(CONFIG_PATH):
with open(CONFIG_PATH, "r") as f:
return json.load(f)
return {}
def save_config(config):
with open(CONFIG_PATH, "w") as f:
json.dump(config, f, indent=2)
f.write("\n")
def list_collections():
ignore = {"old", "_archive", "thumbs"}
# Also read ignore list from site.json
config = load_config()
collections_config = config.get("collections", {})
ignore.update(collections_config.get("ignore", []))
collections = []
if not os.path.isdir(IMAGES_DIR):
return collections
for name in sorted(os.listdir(IMAGES_DIR)):
path = os.path.join(IMAGES_DIR, name)
if os.path.isdir(path) and name not in ignore:
collections.append(name)
return collections
def list_images(collection):
coll_dir = os.path.join(IMAGES_DIR, collection)
if not os.path.isdir(coll_dir):
print(f"Error: collection '{collection}' not found", file=sys.stderr)
sys.exit(1)
images = []
for name in sorted(os.listdir(coll_dir)):
if name == "thumbs" or name.endswith(".md"):
continue
filepath = os.path.join(coll_dir, name)
if os.path.isfile(filepath):
images.append(name)
return images
def rename_image(collection, old_name, new_name):
coll_dir = os.path.join(IMAGES_DIR, collection)
thumbs_dir = os.path.join(coll_dir, "thumbs")
if not os.path.isdir(coll_dir):
print(f"Error: collection '{collection}' not found", file=sys.stderr)
sys.exit(1)
old_jpg = old_name + ".jpg"
new_jpg = new_name + ".jpg"
old_md = old_name + ".md"
new_md = new_name + ".md"
moved = []
# Full-res
src = os.path.join(coll_dir, old_jpg)
dst = os.path.join(coll_dir, new_jpg)
if os.path.exists(src):
os.rename(src, dst)
moved.append(f" {old_jpg} -> {new_jpg}")
else:
print(f"Warning: {src} not found", file=sys.stderr)
# Thumbnail
src = os.path.join(thumbs_dir, old_jpg)
dst = os.path.join(thumbs_dir, new_jpg)
if os.path.exists(src):
os.rename(src, dst)
moved.append(f" thumbs/{old_jpg} -> thumbs/{new_jpg}")
# Markdown sidecar
src = os.path.join(coll_dir, old_md)
dst = os.path.join(coll_dir, new_md)
if os.path.exists(src):
os.rename(src, dst)
moved.append(f" {old_md} -> {new_md}")
# Update site.json collections order if present
config = load_config()
collections_config = config.get("collections", {})
order = collections_config.get("order", [])
# Collection-level order does not track individual images,
# so no update needed here unless a per-image order is added later.
if moved:
print(f"Renamed '{old_name}' -> '{new_name}' in {collection}/:")
for m in moved:
print(m)
else:
print("Nothing was renamed.")
def rename_collection(old_name, new_name):
old_dir = os.path.join(IMAGES_DIR, old_name)
new_dir = os.path.join(IMAGES_DIR, new_name)
if not os.path.isdir(old_dir):
print(f"Error: collection '{old_name}' not found", file=sys.stderr)
sys.exit(1)
if os.path.exists(new_dir):
print(f"Error: '{new_name}' already exists", file=sys.stderr)
sys.exit(1)
os.rename(old_dir, new_dir)
print(f"Renamed directory: {old_name}/ -> {new_name}/")
# Update site.json collections order
config = load_config()
collections_config = config.get("collections", {})
order = collections_config.get("order", [])
if old_name in order:
order = [new_name if n == old_name else n for n in order]
collections_config["order"] = order
config["collections"] = collections_config
save_config(config)
print(f"Updated site.json: '{old_name}' -> '{new_name}'")
def main():
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
cmd = sys.argv[1]
if cmd == "list":
if len(sys.argv) >= 3:
collection = sys.argv[2]
images = list_images(collection)
print(f"{collection}/ ({len(images)} images):")
for img in images:
print(f" {img}")
else:
collections = list_collections()
for coll in collections:
images = list_images(coll)
print(f"{coll}/ ({len(images)} images)")
elif cmd == "image":
if len(sys.argv) != 5:
print("Usage: rename.py image <collection> <old-name> <new-name>")
sys.exit(1)
rename_image(sys.argv[2], sys.argv[3], sys.argv[4])
elif cmd == "collection":
if len(sys.argv) != 4:
print("Usage: rename.py collection <old-name> <new-name>")
sys.exit(1)
rename_collection(sys.argv[2], sys.argv[3])
else:
print(f"Unknown command: {cmd}")
print(__doc__)
sys.exit(1)
if __name__ == "__main__":
main()