feat: Add inpainting (局部重绘) feature for timeline editor
- Add canvas-based mask drawing tools (brush, eraser, rect, lasso) - Add undo/redo history support for mask editing - Integrate inpainting UI into preview player - Add backend API endpoint for inpainting requests - Add MediaService.inpaint method with ComfyUI workflow support - Add Flux inpainting workflows for selfhost and RunningHub 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -285,3 +285,76 @@ class MediaService(ComfyBaseService):
|
||||
except Exception as e:
|
||||
logger.error(f"Media generation error: {e}")
|
||||
raise
|
||||
|
||||
async def inpaint(
|
||||
self,
|
||||
image_path: str,
|
||||
mask_path: str,
|
||||
prompt: Optional[str] = None,
|
||||
denoise_strength: float = 0.8,
|
||||
workflow: Optional[str] = None,
|
||||
**params
|
||||
) -> MediaResult:
|
||||
"""
|
||||
Inpaint image using mask
|
||||
|
||||
Args:
|
||||
image_path: Path to original image
|
||||
mask_path: Path to mask image (white=inpaint, black=keep)
|
||||
prompt: Optional prompt for inpainted region
|
||||
denoise_strength: How much to change masked area (0.0-1.0)
|
||||
workflow: Inpainting workflow to use
|
||||
|
||||
Returns:
|
||||
MediaResult with inpainted image URL
|
||||
"""
|
||||
logger.info(f"🎨 Inpainting image: {image_path}")
|
||||
logger.info(f" Mask: {mask_path}")
|
||||
logger.info(f" Denoise: {denoise_strength}")
|
||||
|
||||
try:
|
||||
# Resolve workflow
|
||||
if workflow is None:
|
||||
workflow = "selfhost/image_inpaint_flux.json"
|
||||
|
||||
workflow_info = self._resolve_workflow(workflow=workflow)
|
||||
|
||||
# Build workflow parameters
|
||||
workflow_params = {
|
||||
"image": image_path,
|
||||
"mask": mask_path,
|
||||
"denoise": denoise_strength,
|
||||
}
|
||||
|
||||
if prompt:
|
||||
workflow_params["prompt"] = prompt
|
||||
|
||||
workflow_params.update(params)
|
||||
|
||||
logger.debug(f"Inpaint workflow parameters: {workflow_params}")
|
||||
|
||||
# Execute workflow
|
||||
kit = await self.core._get_or_create_comfykit()
|
||||
|
||||
if workflow_info["source"] == "runninghub" and "workflow_id" in workflow_info:
|
||||
workflow_input = workflow_info["workflow_id"]
|
||||
else:
|
||||
workflow_input = workflow_info["path"]
|
||||
|
||||
result = await kit.execute(workflow_input, workflow_params)
|
||||
|
||||
if not result.images:
|
||||
logger.error("No image generated from inpainting")
|
||||
raise Exception("Inpainting failed - no image generated")
|
||||
|
||||
image_url = result.images[0]
|
||||
logger.info(f"✅ Inpainted image: {image_url}")
|
||||
|
||||
return MediaResult(
|
||||
media_type="image",
|
||||
url=image_url
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Inpainting error: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user