Files
ai-game-2/tools/ora_editor/routes/krita.py
Bryce fb812e57bc Restructure ORA editor into modular blueprints with browse dialog
- Split app.py into route blueprints (files, layers, images, polygon, mask, krita)
- Create services layer (polygon_storage, comfyui, file_browser)
- Extract config constants to config.py
- Split templates into Jinja partials (base, components, modals)
- Add browse dialog for visual file navigation
- Add /api/browse endpoint for directory listing
2026-03-27 21:29:27 -07:00

109 lines
3.5 KiB
Python

"""Krita integration routes for ORA Editor."""
import os
import shutil
import time
import zipfile
from pathlib import Path
from flask import Blueprint, request, jsonify
from PIL import Image, ImageDraw
from ora_editor.config import TEMP_DIR
import logging
logger = logging.getLogger(__name__)
krita_bp = Blueprint('krita', __name__)
@krita_bp.route('/api/krita/open', methods=['POST'])
def api_krita_open():
"""Open in Krita - behavior depends on mode.
Review mode: Open the full ORA file for layer editing.
Add mode: Export base image with polygon overlay for manual annotation.
"""
data = request.get_json()
if not data or 'ora_path' not in data:
return jsonify({'success': False, 'error': 'Missing ora_path parameter'}), 400
ora_path = data['ora_path']
try:
if data.get('open_full_ora'):
timestamp = str(int(time.time()))
temp_file = TEMP_DIR / f"edit_{timestamp}.ora"
shutil.copy2(ora_path, temp_file)
return jsonify({
'success': True,
'file_url': f'file://{temp_file}',
'temp_path': str(temp_file),
'base_mtime': os.path.getmtime(temp_file)
})
elif data.get('open_base_with_polygon'):
from ora_editor.services import polygon_storage
points = data.get('points', [])
color = data.get('color', '#FF0000')
width = data.get('width', 2)
with zipfile.ZipFile(ora_path, 'r') as zf:
img = Image.open(zf.open('mergedimage.png')).convert('RGBA')
if points and len(points) >= 3:
w, h = img.size
pixel_points = [(int(p['x'] * w), int(p['y'] * h)) for p in points]
draw = ImageDraw.Draw(img)
hex_color = color if len(color) == 7 else color + 'FF'
draw.polygon(pixel_points, outline=hex_color, width=width)
timestamp = str(int(time.time()))
temp_file = TEMP_DIR / f"annotation_{timestamp}.png"
img.save(str(temp_file), format='PNG')
return jsonify({
'success': True,
'file_url': f'file://{temp_file}',
'temp_path': str(temp_file),
'base_mtime': os.path.getmtime(temp_file)
})
else:
return jsonify({'success': False, 'error': 'Invalid request - specify open_full_ora or open_base_with_polygon'}), 400
except Exception as e:
import traceback
logger.error(f"[KRITA OPEN] Error: {e}")
traceback.print_exc()
return jsonify({'success': False, 'error': str(e)}), 500
@krita_bp.route('/api/krita/status/<layer_name>')
def api_krita_status(layer_name):
"""Check if a Krita temp file has been modified."""
temp_files = list(TEMP_DIR.glob(f"{layer_name}_*.png"))
if not temp_files:
return jsonify({'success': False, 'error': 'No temp file found'}), 404
temp_file = max(temp_files, key=lambda f: f.stat().st_mtime)
mtime = temp_file.stat().st_mtime
stored_mtime = request.args.get('stored_mtime')
if stored_mtime:
modified = float(stored_mtime) != mtime
else:
modified = True
return jsonify({
'success': True,
'modified': modified,
'mtime': mtime,
'file_url': f'file://{temp_file}'
})