mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-26 07:35:44 -03:00
Add recipes checkpoint
This commit is contained in:
134
py/routes/recipe_routes.py
Normal file
134
py/routes/recipe_routes.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
from aiohttp import web
|
||||
from typing import Dict
|
||||
|
||||
from ..services.recipe_scanner import RecipeScanner
|
||||
from ..services.lora_scanner import LoraScanner
|
||||
from ..config import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
print("Recipe Routes module loaded", file=sys.stderr)
|
||||
|
||||
class RecipeRoutes:
|
||||
"""API route handlers for Recipe management"""
|
||||
|
||||
def __init__(self):
|
||||
print("Initializing RecipeRoutes", file=sys.stderr)
|
||||
self.recipe_scanner = RecipeScanner(LoraScanner())
|
||||
|
||||
# Pre-warm the cache
|
||||
self._init_cache_task = None
|
||||
|
||||
@classmethod
|
||||
def setup_routes(cls, app: web.Application):
|
||||
"""Register API routes"""
|
||||
print("Setting up recipe routes", file=sys.stderr)
|
||||
routes = cls()
|
||||
app.router.add_get('/api/recipes', routes.get_recipes)
|
||||
app.router.add_get('/api/recipe/{recipe_id}', routes.get_recipe_detail)
|
||||
|
||||
# Start cache initialization
|
||||
app.on_startup.append(routes._init_cache)
|
||||
|
||||
print("Recipe routes setup complete", file=sys.stderr)
|
||||
|
||||
async def _init_cache(self, app):
|
||||
"""Initialize cache on startup"""
|
||||
print("Pre-warming recipe cache...", file=sys.stderr)
|
||||
try:
|
||||
# Diagnose lora scanner first
|
||||
await self.recipe_scanner._lora_scanner.diagnose_hash_index()
|
||||
|
||||
# Force a cache refresh
|
||||
await self.recipe_scanner.get_cached_data(force_refresh=True)
|
||||
print("Recipe cache pre-warming complete", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"Error pre-warming recipe cache: {e}", file=sys.stderr)
|
||||
|
||||
async def get_recipes(self, request: web.Request) -> web.Response:
|
||||
"""API endpoint for getting paginated recipes"""
|
||||
try:
|
||||
print("API: GET /api/recipes", file=sys.stderr)
|
||||
# Get query parameters with defaults
|
||||
page = int(request.query.get('page', '1'))
|
||||
page_size = int(request.query.get('page_size', '20'))
|
||||
sort_by = request.query.get('sort_by', 'date')
|
||||
search = request.query.get('search', None)
|
||||
|
||||
# Get paginated data
|
||||
result = await self.recipe_scanner.get_paginated_data(
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
sort_by=sort_by,
|
||||
search=search
|
||||
)
|
||||
|
||||
# Format the response data with static URLs for file paths
|
||||
for item in result['items']:
|
||||
item['preview_url'] = item['file_path']
|
||||
# Convert file path to URL
|
||||
item['file_url'] = self._format_recipe_file_url(item['file_path'])
|
||||
|
||||
return web.json_response(result)
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving recipes: {e}", exc_info=True)
|
||||
print(f"API Error: {e}", file=sys.stderr)
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
async def get_recipe_detail(self, request: web.Request) -> web.Response:
|
||||
"""Get detailed information about a specific recipe"""
|
||||
try:
|
||||
recipe_id = request.match_info['recipe_id']
|
||||
|
||||
# Get all recipes from cache
|
||||
cache = await self.recipe_scanner.get_cached_data()
|
||||
|
||||
# Find the specific recipe
|
||||
recipe = next((r for r in cache.raw_data if str(r.get('id', '')) == recipe_id), None)
|
||||
|
||||
if not recipe:
|
||||
return web.json_response({"error": "Recipe not found"}, status=404)
|
||||
|
||||
# Format recipe data
|
||||
formatted_recipe = self._format_recipe_data(recipe)
|
||||
|
||||
return web.json_response(formatted_recipe)
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving recipe details: {e}", exc_info=True)
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
def _format_recipe_file_url(self, file_path: str) -> str:
|
||||
"""Format file path for recipe image as a URL"""
|
||||
# This is a simplified example - in real implementation,
|
||||
# you would map this to a static route that can serve the file
|
||||
|
||||
# For recipes folder in the first lora root
|
||||
for idx, root in enumerate(config.loras_roots, start=1):
|
||||
recipes_dir = os.path.join(root, "recipes")
|
||||
if file_path.startswith(recipes_dir):
|
||||
relative_path = os.path.relpath(file_path, root)
|
||||
return f"/loras_static/root{idx}/{relative_path}"
|
||||
|
||||
return file_path # Return original path if no mapping found
|
||||
|
||||
def _format_recipe_data(self, recipe: Dict) -> Dict:
|
||||
"""Format recipe data for API response"""
|
||||
formatted = {**recipe} # Copy all fields
|
||||
|
||||
# Format file paths to URLs
|
||||
if 'file_path' in formatted:
|
||||
formatted['file_url'] = self._format_recipe_file_url(formatted['file_path'])
|
||||
|
||||
# Format dates for display
|
||||
for date_field in ['created_date', 'modified']:
|
||||
if date_field in formatted:
|
||||
formatted[f"{date_field}_formatted"] = self._format_timestamp(formatted[date_field])
|
||||
|
||||
return formatted
|
||||
|
||||
def _format_timestamp(self, timestamp: float) -> str:
|
||||
"""Format timestamp for display"""
|
||||
from datetime import datetime
|
||||
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
|
||||
Reference in New Issue
Block a user