- 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
109 lines
3.5 KiB
Python
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}'
|
|
})
|