feat: initial ComfyUI workflow templates extension

- 40 workflow templates (text-to-image, image-to-video, image-to-image,
  text-to-music, upscaling, advanced, nsfw)
- Color-coded placeholder preview images using Pillow
- Template-only extension (no custom nodes)
- Preview generation script for future workflow additions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-26 09:16:45 +01:00
commit d6dfdd72e2
86 changed files with 43550 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Generate placeholder preview images for ComfyUI workflow templates."""
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
PREVIEW_WIDTH = 512
PREVIEW_HEIGHT = 288 # 16:9 aspect ratio
# Category colors based on filename patterns
# Order matters: more specific patterns first, generic ones last
CATEGORY_COLORS = {
# NSFW patterns (check first - most specific)
"lustify": "#9B2C2C", # Dark red - NSFW
"realvis": "#9B2C2C", # Dark red - NSFW
"pony": "#9B2C2C", # Dark red - NSFW
"nsfw": "#9B2C2C", # Dark red - NSFW
# Advanced patterns
"animatediff": "#4ad9d9", # Cyan - advanced
"controlnet": "#4ad9d9", # Cyan - advanced
"batch": "#4ad9d9", # Cyan - advanced
# Upscaling
"upscale": "#9b4ad9", # Purple - upscaling
# Generic type patterns (check last)
"t2i": "#4a90d9", # Blue - text-to-image
"i2v": "#d94a4a", # Red - image-to-video
"i2i": "#4ad96b", # Green - image-to-image
"t2m": "#d9a04a", # Orange - text-to-music
"m2m": "#d9a04a", # Orange - music-to-music
}
DEFAULT_COLOR = "#2d2d2d"
def get_color_for_workflow(filename: str) -> str:
"""Determine background color based on filename."""
lower = filename.lower()
for pattern, color in CATEGORY_COLORS.items():
if pattern in lower:
return color
return DEFAULT_COLOR
def humanize_filename(filename: str) -> str:
"""Convert filename to readable title."""
# Remove extension and common suffixes
name = filename.replace('.json', '')
name = name.replace('-production-v1', '')
name = name.replace('-production', '')
name = name.replace('-v1', '')
# Replace separators with spaces
name = name.replace('-', ' ').replace('_', ' ')
# Title case
return name.title()
def generate_preview(json_path: Path, output_path: Path):
"""Generate a placeholder preview image with workflow title."""
filename = json_path.name
bg_color = get_color_for_workflow(filename)
title = humanize_filename(filename)
# Create image
img = Image.new('RGB', (PREVIEW_WIDTH, PREVIEW_HEIGHT), bg_color)
draw = ImageDraw.Draw(img)
# Try to load fonts
try:
title_font = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 24
)
except OSError:
title_font = ImageFont.load_default()
# Word wrap title
words = title.split()
lines = []
current_line = ""
for word in words:
test = f"{current_line} {word}".strip()
if len(test) > 28:
if current_line:
lines.append(current_line)
current_line = word
else:
current_line = test
if current_line:
lines.append(current_line)
# Draw centered text
y_offset = PREVIEW_HEIGHT // 2 - (len(lines) * 30) // 2
for line in lines:
bbox = draw.textbbox((0, 0), line, font=title_font)
text_width = bbox[2] - bbox[0]
x = (PREVIEW_WIDTH - text_width) // 2
draw.text((x, y_offset), line, fill="white", font=title_font)
y_offset += 30
img.save(output_path, 'JPEG', quality=85)
def main():
workflows_dir = Path(__file__).parent.parent / 'example_workflows'
count = 0
for json_file in sorted(workflows_dir.glob('*.json')):
preview_path = json_file.with_suffix('.jpg')
if preview_path.exists():
print(f"Skipping (exists): {json_file.name}")
continue
generate_preview(json_file, preview_path)
print(f"Generated: {preview_path.name}")
count += 1
print(f"\nDone! Generated {count} preview images.")
if __name__ == '__main__':
main()