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
177 lines
5.8 KiB
Python
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())
|