Files
talemate/docs/cleanup.py
veguAI f5d41c04c8 0.37.0 (#267)
0.37.0

- **Director Planning** — Multi-step todo lists in director chat plus a Generate long progress action for multi-beat scene arcs.
- **Auto Narration** — Unified auto-narration replacing the old Narrate after Dialogue toggle, with a chance slider and weighted action mix.
- **LLM Prompt Templates Manager** — Dedicated UI tab for viewing, creating, editing, and deleting prompt templates.
- **Character Folders** — Collapsible folders in the World Editor character list, synced across linked scenes.
- **OpenAI Compatible TTS** — Connect any number of OpenAI-compatible TTS servers in parallel.
- **KoboldCpp TTS Auto-Setup** — KoboldCpp clients with a TTS model loaded register themselves as a TTS backend.
- **Model Testing Harness** — Bundled scene that runs basic capability tests against any connected LLM.

Plus 27 improvements and 28 bug fixes
2026-05-12 21:01:51 +03:00

188 lines
6.3 KiB
Python

import os
import re
import subprocess
import argparse
EXCLUDED_DIRS = {"update-progress"}
REFERENCE_PATTERNS = [
r"!\[.*?\]\((.*?)\)", # markdown ![alt](url)
r"""<img[^>]*\bsrc=["']([^"']+)["']""", # html <img src="...">
]
def find_image_references(md_file):
"""Find all image references in a markdown file (markdown + HTML syntax)."""
with open(md_file, "r", encoding="utf-8") as f:
content = f.read()
cleaned_paths = []
for pattern in REFERENCE_PATTERNS:
for match in re.findall(pattern, content):
path = match.lstrip("/")
if "img/" in path:
cleaned_paths.append(path[path.index("img/") + 4 :])
return cleaned_paths
def scan_markdown_files(docs_dir, project_root):
"""Recursively scan markdown files in docs_dir, plus root-level *.md files."""
md_files = []
for root, dirs, files in os.walk(docs_dir):
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
for file in files:
if file.endswith(".md"):
md_files.append(os.path.join(root, file))
for file in os.listdir(project_root):
full_path = os.path.join(project_root, file)
if file.endswith(".md") and os.path.isfile(full_path):
md_files.append(full_path)
return md_files
def find_img_dirs(docs_dir):
"""Yield every directory named 'img' under docs_dir, excluding EXCLUDED_DIRS."""
for root, dirs, _ in os.walk(docs_dir):
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
for d in dirs:
if d == "img":
yield os.path.join(root, d)
def find_all_images(img_dir):
"""Yield (img_dir, rel_path) for every image under img_dir."""
for root, _, files in os.walk(img_dir):
for file in files:
if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".svg")):
rel_path = os.path.relpath(os.path.join(root, file), img_dir)
yield (img_dir, rel_path)
def grep_check_image(docs_dir, image_path):
"""
Check if an image is referenced anywhere using grep.
Returns True if any reference is found, False otherwise.
"""
try:
# Build a pattern that requires each path segment in order with any
# separator between them — works for both versioned (0.37.0/foo.png)
# and flat (subdir/foo.png) images.
parts = os.path.normpath(image_path).split(os.sep)
pattern = ".*".join(parts)
try:
exclude_args = [f"--exclude-dir={d}" for d in EXCLUDED_DIRS]
result = subprocess.run(
["grep", "-r", "-l", *exclude_args, pattern, docs_dir],
capture_output=True,
text=True,
)
if result.stdout.strip():
print(f"Found reference to {image_path} with pattern: {pattern}")
return True
except subprocess.CalledProcessError:
pass
except Exception as e:
print(f"Error during grep check for {image_path}: {e}")
return False
def main():
parser = argparse.ArgumentParser(
description="Find and optionally delete unused images in the MkDocs project"
)
parser.add_argument("--delete", action="store_true", help="Delete unused images")
parser.add_argument(
"--verbose", action="store_true", help="Show all found references and files"
)
parser.add_argument(
"--skip-grep", action="store_true", help="Skip the additional grep validation"
)
args = parser.parse_args()
docs_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(docs_dir)
img_dirs = list(find_img_dirs(docs_dir))
print(f"Scanning markdown files in: {docs_dir} (+ root-level *.md)")
print(f"Found {len(img_dirs)} img directories:")
for d in img_dirs:
print(f" - {os.path.relpath(d, project_root)}")
# Get all markdown files
md_files = scan_markdown_files(docs_dir, project_root)
print(f"Found {len(md_files)} markdown files")
# Collect all image references
used_images = set()
for md_file in md_files:
refs = find_image_references(md_file)
used_images.update(refs)
# Collect every physical image across every img dir
all_image_entries = [
entry for img_dir in img_dirs for entry in find_all_images(img_dir)
]
if args.verbose:
print("\nAll image references found in markdown:")
for img in sorted(used_images):
print(f"- {img}")
print("\nAll images on disk:")
for img_dir, rel_path in sorted(all_image_entries):
print(f"- {os.path.relpath(os.path.join(img_dir, rel_path), project_root)}")
# Find potentially unused images
unused_entries = [
(img_dir, rel_path)
for img_dir, rel_path in all_image_entries
if rel_path not in used_images
]
# Additional grep validation if not skipped
if not args.skip_grep and unused_entries:
print("\nPerforming additional grep validation...")
actually_unused = [
(img_dir, rel_path)
for img_dir, rel_path in unused_entries
if not grep_check_image(docs_dir, rel_path)
]
if len(actually_unused) != len(unused_entries):
print(
f"\nGrep validation found {len(unused_entries) - len(actually_unused)} additional image references!"
)
unused_entries = actually_unused
# Report findings
print("\nResults:")
print(f"Total images found: {len(all_image_entries)}")
print(f"Images referenced in markdown: {len(used_images)}")
print(f"Unused images: {len(unused_entries)}")
if unused_entries:
print("\nUnused images:")
for img_dir, rel_path in sorted(unused_entries):
print(f"- {os.path.relpath(os.path.join(img_dir, rel_path), project_root)}")
if args.delete:
print("\nDeleting unused images...")
for img_dir, rel_path in unused_entries:
full_path = os.path.join(img_dir, rel_path)
try:
os.remove(full_path)
print(f"Deleted: {os.path.relpath(full_path, project_root)}")
except Exception as e:
print(f"Error deleting {full_path}: {e}")
print("\nDeletion complete")
else:
print("\nNo unused images found!")
if __name__ == "__main__":
main()