Add update functionality for recipe metadata in RecipeRoutes and RecipeModal

- Introduced a new API endpoint to update recipe metadata, allowing users to modify recipe titles and tags.
- Enhanced RecipeModal to support inline editing of recipe titles and tags, improving user interaction.
- Updated RecipeCard to reflect changes in recipe metadata, ensuring consistency across the application.
- Improved error handling for metadata updates to provide clearer feedback to users.
This commit is contained in:
Will Miao
2025-03-29 18:35:49 +08:00
parent 069ebce895
commit b65350b7cb
6 changed files with 653 additions and 79 deletions

View File

@@ -50,6 +50,9 @@ class RecipeRoutes:
# Add new endpoint for getting recipe syntax
app.router.add_get('/api/recipe/{recipe_id}/syntax', routes.get_recipe_syntax)
# Add new endpoint for updating recipe metadata (name and tags)
app.router.add_put('/api/recipe/{recipe_id}/update', routes.update_recipe)
# Start cache initialization
app.on_startup.append(routes._init_cache)
@@ -984,3 +987,30 @@ class RecipeRoutes:
except Exception as e:
logger.error(f"Error generating recipe syntax: {e}", exc_info=True)
return web.json_response({"error": str(e)}, status=500)
async def update_recipe(self, request: web.Request) -> web.Response:
"""Update recipe metadata (name and tags)"""
try:
recipe_id = request.match_info['recipe_id']
data = await request.json()
# Validate required fields
if 'title' not in data and 'tags' not in data:
return web.json_response({
"error": "At least one field to update must be provided (title or tags)"
}, status=400)
# Use the recipe scanner's update method
success = await self.recipe_scanner.update_recipe_metadata(recipe_id, data)
if not success:
return web.json_response({"error": "Recipe not found or update failed"}, status=404)
return web.json_response({
"success": True,
"recipe_id": recipe_id,
"updates": data
})
except Exception as e:
logger.error(f"Error updating recipe: {e}", exc_info=True)
return web.json_response({"error": str(e)}, status=500)

View File

@@ -37,18 +37,18 @@ class RecipeCache:
Returns:
bool: True if the update was successful, False if the recipe wasn't found
"""
async with self._lock:
# Update in raw_data
for item in self.raw_data:
if item.get('id') == recipe_id:
item.update(metadata)
break
else:
return False # Recipe not found
# Resort to reflect changes
await self.resort()
return True
# Update in raw_data
for item in self.raw_data:
if item.get('id') == recipe_id:
item.update(metadata)
break
else:
return False # Recipe not found
# Resort to reflect changes
await self.resort()
return True
async def add_recipe(self, recipe_data: Dict) -> None:
"""Add a new recipe to the cache

View File

@@ -431,6 +431,54 @@ class RecipeScanner:
return result
async def update_recipe_metadata(self, recipe_id: str, metadata: dict) -> bool:
"""Update recipe metadata (like title and tags) in both file system and cache
Args:
recipe_id: The ID of the recipe to update
metadata: Dictionary containing metadata fields to update (title, tags, etc.)
Returns:
bool: True if successful, False otherwise
"""
import os
import json
# First, find the recipe JSON file path
recipe_json_path = os.path.join(self.recipes_dir, f"{recipe_id}.recipe.json")
if not os.path.exists(recipe_json_path):
return False
try:
# Load existing recipe data
with open(recipe_json_path, 'r', encoding='utf-8') as f:
recipe_data = json.load(f)
# Update fields
for key, value in metadata.items():
recipe_data[key] = value
# Save updated recipe
with open(recipe_json_path, 'w', encoding='utf-8') as f:
json.dump(recipe_data, f, indent=4, ensure_ascii=False)
# Update the cache if it exists
if self._cache is not None:
await self._cache.update_recipe_metadata(recipe_id, metadata)
# If the recipe has an image, update its EXIF metadata
from ..utils.exif_utils import ExifUtils
image_path = recipe_data.get('file_path')
if image_path and os.path.exists(image_path):
ExifUtils.append_recipe_metadata(image_path, recipe_data)
return True
except Exception as e:
import logging
logging.getLogger(__name__).error(f"Error updating recipe metadata: {e}", exc_info=True)
return False
async def update_lora_filename_by_hash(self, hash_value: str, new_file_name: str) -> Tuple[int, int]:
"""Update file_name in all recipes that contain a LoRA with the specified hash.