Files
ubuntu-post-install/paintplus/backend/scripts/seed_eye_catalog.py
T
Claude 084922afaa paintplus: vendor the app source and rename from EditmaskwithAI
Bring the full EditmaskwithAI application into the repo under paintplus/
(429 files) so the service is self-contained — the installer copies the
vendored source to ~/docker/paintplus/src instead of cloning at runtime.

Rename to PaintPlus (service + branding; app logic untouched):
- services/editmaskwithai.sh -> services/paintplus.sh (register_service
  paintplus, install_paintplus, ~/docker/paintplus, Caddy paintplus:8000,
  Authelia option preserved)
- container names -> paintplus across docker-compose*.yml; dev network
  -> paintplus-network
- browser <title> -> "PaintPlus - AI Image Editor"; README heading ->
  PaintPlus with upstream provenance note
- README utilities table: editmaskwithai -> paintplus

Backend/frontend code (help strings referencing the old container name,
the ai_photo_edit.db filename) is intentionally left as-is to avoid
touching application logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
2026-06-26 05:48:43 +00:00

177 lines
5.8 KiB
Python

"""
Seed the patch library with classical carved eyes from public domain sources
This script helps pre-populate the eye catalog with examples from:
- Greek statues (Metropolitan Museum, Louvre)
- Roman sculptures (Smithsonian, British Museum)
- Renaissance carvings
- Ancient Egyptian carved eyes
All images should be public domain (CC0, Public Domain Mark)
"""
import asyncio
import httpx
from pathlib import Path
from PIL import Image
from io import BytesIO
# Public domain eye examples to seed the catalog
# These are examples - you would add actual URLs from museum APIs
CLASSICAL_EYES = [
{
"name": "Greek Statue - Serene Left Eye",
"description": "Classical Greek marble carving, convex eyeball, defined upper lid, deep socket. Perfect for serene expressions.",
"category": "carved_eye",
"tags": "greek, serene, left, marble, classical, convex, deep-socket",
"style": "greek_classical",
"emotion": "serene",
"side": "left",
"source_url": "https://images.metmuseum.org/...", # Example
"source": "Metropolitan Museum of Art - Public Domain"
},
{
"name": "Roman Sculpture - Fierce Right Eye",
"description": "Roman marble, prominent brow ridge, intense gaze, sharp eyelid definition.",
"category": "carved_eye",
"tags": "roman, fierce, right, marble, intense, sharp-detail",
"style": "roman_classical",
"emotion": "fierce",
"side": "right",
"source_url": "https://...",
"source": "Smithsonian - CC0"
},
{
"name": "Greek Kouros - Peaceful Left Eye",
"description": "Archaic Greek style, almond-shaped, subtle carving, peaceful expression.",
"category": "carved_eye",
"tags": "greek, peaceful, left, archaic, almond-shaped, subtle",
"style": "greek_archaic",
"emotion": "peaceful",
"side": "left",
"source_url": "https://...",
"source": "Getty Museum - Public Domain"
},
{
"name": "Roman Portrait - Wise Right Eye",
"description": "Late Roman period, detailed eyelids, slight downward gaze, wisdom and age.",
"category": "carved_eye",
"tags": "roman, wise, right, portrait, detailed, aged",
"style": "roman_portrait",
"emotion": "wise",
"side": "right",
"source_url": "https://...",
"source": "British Museum - CC0"
},
]
async def download_image(url: str) -> bytes:
"""Download image from URL"""
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
return response.content
async def crop_eye_from_statue(image_bytes: bytes, crop_box: tuple) -> bytes:
"""
Crop just the eye from a full statue photo
Args:
image_bytes: Full statue image
crop_box: (left, top, right, bottom) coordinates
Returns:
Cropped eye image bytes
"""
img = Image.open(BytesIO(image_bytes))
eye = img.crop(crop_box)
# Save as PNG
buffer = BytesIO()
eye.save(buffer, format='PNG')
return buffer.getvalue()
async def seed_eye_catalog():
"""
Seed the patch library with classical carved eyes
NOTE: This is a template. You need to:
1. Get actual public domain image URLs
2. Manually crop the eyes (or provide crop coordinates)
3. Run this to populate the catalog
"""
from app.database import SessionLocal
from app.models.patch import Patch
from app.services.patch_library import PatchLibraryService
db = SessionLocal()
patch_service = PatchLibraryService()
print("Seeding eye catalog with classical carved eyes...")
for eye_data in CLASSICAL_EYES:
print(f"\nAdding: {eye_data['name']}")
# Create patch record
patch = Patch(
name=eye_data['name'],
description=eye_data['description'],
source_type="imported",
category=eye_data['category'],
tags=eye_data['tags'],
width=0, # Will be set after image save
height=0,
user_id=None,
file_path=""
)
db.add(patch)
db.commit()
db.refresh(patch)
# Download and save image
# NOTE: You need to manually download/crop these first
# This is just the structure
try:
# image_bytes = await download_image(eye_data['source_url'])
# cropped_eye = await crop_eye_from_statue(image_bytes, crop_box)
# For now, you would manually place images in:
# ./seed_data/eyes/greek_serene_left.png
# ./seed_data/eyes/roman_fierce_right.png
# etc.
seed_image_path = Path(__file__).parent / "seed_data" / "eyes" / f"{eye_data['style']}_{eye_data['emotion']}_{eye_data['side']}.png"
if seed_image_path.exists():
file_path = patch_service.save_patch_from_file(
patch.id,
str(seed_image_path),
create_thumb=True
)
# Get dimensions
width, height = patch_service.get_patch_size(patch.id)
patch.width = width
patch.height = height
patch.file_path = file_path
patch.thumbnail_path = str(patch_service.get_thumbnail_path(patch.id))
db.commit()
print(f"✅ Added {eye_data['name']}")
else:
print(f"⚠️ Image not found: {seed_image_path}")
print(f" Please download and crop eye from: {eye_data['source']}")
except Exception as e:
print(f"❌ Error adding {eye_data['name']}: {e}")
db.rollback()
db.close()
print("\n✅ Eye catalog seeding complete!")
if __name__ == "__main__":
asyncio.run(seed_eye_catalog())