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
176 lines
5.1 KiB
Python
176 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Startup script to import classic eyes into the patch library.
|
|
This should be run once when the backend starts, or manually.
|
|
|
|
Usage:
|
|
python scripts/startup_import_eyes.py
|
|
|
|
This script:
|
|
1. Converts PPM files to PNG (if needed)
|
|
2. Imports all classic eyes into the patch library database
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
|
|
# Add parent directory to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "backend"))
|
|
|
|
try:
|
|
from PIL import Image
|
|
from sqlalchemy.orm import Session
|
|
from app.database import SessionLocal, engine, Base
|
|
from app.models.patch import Patch
|
|
from app.services.patch_library import PatchLibraryService
|
|
except ImportError as e:
|
|
print(f"Error: Required modules not available: {e}")
|
|
print("Run this script inside the backend container.")
|
|
sys.exit(1)
|
|
|
|
|
|
def convert_ppm_to_png(ppm_dir: Path) -> int:
|
|
"""Convert all PPM files to PNG in the given directory."""
|
|
converted = 0
|
|
for ppm_file in ppm_dir.glob("*.ppm"):
|
|
png_file = ppm_file.with_suffix('.png')
|
|
if not png_file.exists():
|
|
try:
|
|
img = Image.open(ppm_file)
|
|
img.save(png_file, 'PNG')
|
|
converted += 1
|
|
print(f" Converted: {ppm_file.name} -> {png_file.name}")
|
|
except Exception as e:
|
|
print(f" Error converting {ppm_file.name}: {e}")
|
|
return converted
|
|
|
|
|
|
def import_eyes(source_dir: Path, db: Session, patch_service: PatchLibraryService) -> int:
|
|
"""Import eye images from the given directory."""
|
|
metadata_path = source_dir / "metadata.json"
|
|
|
|
if not metadata_path.exists():
|
|
print(f" No metadata.json found in {source_dir}")
|
|
return 0
|
|
|
|
with open(metadata_path, 'r') as f:
|
|
metadata = json.load(f)
|
|
|
|
imported = 0
|
|
|
|
for item in metadata:
|
|
try:
|
|
# Try PNG first, then PPM
|
|
filename = item.get('filename', item.get('ppm_filename', '')).replace('.ppm', '.png')
|
|
filepath = source_dir / filename
|
|
|
|
if not filepath.exists():
|
|
# Try PPM
|
|
filepath = source_dir / item.get('ppm_filename', filename.replace('.png', '.ppm'))
|
|
|
|
if not filepath.exists():
|
|
continue
|
|
|
|
name = item['name']
|
|
|
|
# Check if already exists
|
|
existing = db.query(Patch).filter(Patch.name == name).first()
|
|
if existing:
|
|
continue # Silent skip for duplicates on startup
|
|
|
|
# Load and convert if PPM
|
|
img = Image.open(filepath).convert('RGBA')
|
|
|
|
# Create database record
|
|
patch = Patch(
|
|
name=name,
|
|
description=item.get('description', ''),
|
|
source_type="imported",
|
|
width=img.width,
|
|
height=img.height,
|
|
tags=item.get('tags', 'eye,classic'),
|
|
category="eye",
|
|
is_public=True,
|
|
file_path="",
|
|
thumbnail_path=""
|
|
)
|
|
db.add(patch)
|
|
db.flush()
|
|
|
|
# Save to patch library as PNG
|
|
patch_path = patch_service.get_patch_path(patch.id)
|
|
img.save(patch_path, 'PNG')
|
|
|
|
# Create thumbnail
|
|
thumb_path = patch_service.get_thumbnail_path(patch.id)
|
|
patch_service.create_thumbnail(patch_path, thumb_path)
|
|
|
|
# Update paths
|
|
patch.file_path = f"patch_library/{patch.id}.png"
|
|
patch.thumbnail_path = f"patch_library/{patch.id}_thumb.png"
|
|
|
|
db.commit()
|
|
imported += 1
|
|
print(f" Imported: {name} (ID: {patch.id})")
|
|
|
|
except Exception as e:
|
|
db.rollback()
|
|
print(f" Error: {e}")
|
|
|
|
return imported
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("Classic Eyes Startup Import")
|
|
print("=" * 60)
|
|
|
|
# Find the classic eyes directory
|
|
data_dir = Path(os.environ.get("DATA_DIR", "/app/data"))
|
|
if not data_dir.exists():
|
|
data_dir = Path(__file__).parent.parent / "data"
|
|
|
|
ppm_dir = data_dir / "classic_eyes_ppm"
|
|
|
|
if not ppm_dir.exists():
|
|
print(f"\nNo classic eyes found at: {ppm_dir}")
|
|
print("Run generate_classic_eyes_ppm.py first.")
|
|
return
|
|
|
|
# Initialize database
|
|
print("\nInitializing database...")
|
|
Base.metadata.create_all(bind=engine)
|
|
db = SessionLocal()
|
|
|
|
# Initialize patch library
|
|
patch_service = PatchLibraryService(str(data_dir))
|
|
|
|
# Convert PPM to PNG
|
|
print("\nConverting PPM to PNG (if needed)...")
|
|
converted = convert_ppm_to_png(ppm_dir)
|
|
if converted > 0:
|
|
print(f" Converted {converted} files")
|
|
else:
|
|
print(" All files already converted")
|
|
|
|
# Import eyes
|
|
print("\nImporting classic eyes...")
|
|
imported = import_eyes(ppm_dir, db, patch_service)
|
|
|
|
# Count existing
|
|
total = db.query(Patch).filter(Patch.category == "eye").count()
|
|
|
|
print("\n" + "=" * 60)
|
|
print(f"Import Complete!")
|
|
print(f" Newly imported: {imported}")
|
|
print(f" Total eye patches in library: {total}")
|
|
print("=" * 60)
|
|
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|