selfit-camera commited on
Commit
68e04c1
·
1 Parent(s): 224425b
Files changed (2) hide show
  1. app.py +130 -36
  2. util.py +26 -16
app.py CHANGED
@@ -9,9 +9,9 @@ from nfsw import NSFWDetector
9
 
10
  # Configuration parameters
11
  FREE_TRY_N = 20 # Free phase: first 20 tries without restrictions
12
- SLOW_TRY_N = 30 # Slow phase start: 30 tries
13
  SLOW2_TRY_N = 40 # Slow phase start: 30 tries
14
- RATE_LIMIT_60 = 75 # Full restriction: blocked after 60 tries
15
 
16
  # Time window configuration (minutes)
17
  PHASE_1_WINDOW = 5 # 20-30 tries: 3 minutes
@@ -192,17 +192,17 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
192
  IP_Dict[client_ip] += 1
193
 
194
  if input_image is None:
195
- return None, "Please upload an image first"
196
 
197
  if not prompt or prompt.strip() == "":
198
- return None, "Please enter editing prompt"
199
 
200
  # Check if prompt length is greater than 3 characters
201
  if len(prompt.strip()) <= 3:
202
- return None, "❌ Editing prompt must be more than 3 characters"
203
  except Exception as e:
204
  print(f"⚠️ Request preprocessing error: {e}")
205
- return None, "❌ Request processing error"
206
 
207
  # Get user current phase
208
  current_phase = get_ip_phase(client_ip)
@@ -212,14 +212,14 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
212
 
213
  # Check if completely blocked
214
  if current_phase == 'blocked':
215
- return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation"
216
 
217
  # Check rate limit (applies to rate_limit phases)
218
  if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']:
219
  is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase)
220
  if is_limited:
221
  wait_minutes_int = int(wait_minutes) + 1
222
- return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again"
223
 
224
  # Handle NSFW detection based on phase
225
  is_nsfw_task = False # Track if this task involves NSFW content
@@ -260,10 +260,10 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
260
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
261
 
262
  # Call image editing processing function
263
- result_url, message = process_image_edit(input_image, prompt.strip(), progress_callback)
264
 
265
  if result_url:
266
- print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}", flush=True)
267
 
268
  # Detect result image NSFW content (only in rate limit phases)
269
  if nsfw_detector is not None and current_phase != 'free':
@@ -314,14 +314,57 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
314
  except Exception as e:
315
  print(f"⚠️ Final progress update failed: {e}")
316
 
317
- return final_result, final_message
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  else:
319
  print(f"❌ Processing failed - IP: {client_ip}, error: {message}", flush=True)
320
- return None, "❌ " + message
321
 
322
  except Exception as e:
323
  print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
324
- return None, f"❌ Error occurred during processing: {str(e)}"
325
 
326
  def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Progress()):
327
  """
@@ -338,27 +381,27 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
338
  IP_Dict[client_ip] += 1
339
 
340
  if image_dict is None:
341
- return None, "Please upload an image and draw the area to edit"
342
 
343
  # Check if background and layers exist
344
  if "background" not in image_dict or "layers" not in image_dict:
345
- return None, "Please draw the area to edit on the image"
346
 
347
  base_image = image_dict["background"]
348
  layers = image_dict["layers"]
349
 
350
  if not layers:
351
- return None, "Please draw the area to edit on the image"
352
 
353
  if not prompt or prompt.strip() == "":
354
- return None, "Please enter editing prompt"
355
 
356
  # Check prompt length
357
  if len(prompt.strip()) <= 3:
358
- return None, "❌ Editing prompt must be more than 3 characters"
359
  except Exception as e:
360
  print(f"⚠️ Local edit request preprocessing error: {e}")
361
- return None, "❌ Request processing error"
362
 
363
  # Get user current phase
364
  current_phase = get_ip_phase(client_ip)
@@ -368,14 +411,14 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
368
 
369
  # Check if completely blocked
370
  if current_phase == 'blocked':
371
- return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation"
372
 
373
  # Check rate limit (applies to rate_limit phases)
374
  if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']:
375
  is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase)
376
  if is_limited:
377
  wait_minutes_int = int(wait_minutes) + 1
378
- return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again"
379
 
380
  # Handle NSFW detection based on phase
381
  is_nsfw_task = False # Track if this task involves NSFW content
@@ -416,10 +459,10 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
416
  print(f"✅ Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
417
 
418
  # Call local image editing processing function
419
- result_url, message = process_local_image_edit(base_image, layers, prompt.strip(), progress_callback)
420
 
421
  if result_url:
422
- print(f"✅ Local editing completed successfully - IP: {client_ip}, result_url: {result_url}", flush=True)
423
 
424
  # Detect result image NSFW content (only in rate limit phases)
425
  if nsfw_detector is not None and current_phase != 'free':
@@ -470,14 +513,57 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
470
  except Exception as e:
471
  print(f"⚠️ Local edit final progress update failed: {e}")
472
 
473
- return final_result, final_message
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  else:
475
  print(f"❌ Local editing processing failed - IP: {client_ip}, error: {message}", flush=True)
476
- return None, "❌ " + message
477
 
478
  except Exception as e:
479
  print(f"❌ Local editing exception - IP: {client_ip}, error: {str(e)}")
480
- return None, f"❌ Error occurred during processing: {str(e)}"
481
 
482
  # Create Gradio interface
483
  def create_app():
@@ -517,16 +603,18 @@ def create_app():
517
  """
518
  ) as app:
519
 
520
- gr.Markdown(
521
- """
522
- # 🎨 AI Image Editor
523
- """,
524
- elem_classes=["main-container"]
525
- )
 
 
526
 
527
- # Powered by line below title
528
  gr.HTML("""
529
- <div style="text-align: center; margin: -10px auto 30px auto;">
530
  <p style="margin: 0; font-size: 16px; color: #999; font-weight: 400;">
531
  powered by <a href="https://omnicreator.net/#generator" target="_blank" style="color: #667eea; text-decoration: none;">omnicreator.net</a>
532
  </p>
@@ -582,6 +670,9 @@ def create_app():
582
  max_lines=3,
583
  interactive=False
584
  )
 
 
 
585
 
586
  # Example area
587
  gr.Markdown("### 💡 Prompt Examples")
@@ -607,7 +698,7 @@ def create_app():
607
  edit_button.click(
608
  fn=edit_image_interface,
609
  inputs=[input_image, prompt_input],
610
- outputs=[output_image, status_output],
611
  show_progress=True,
612
  # Increase concurrency settings
613
  concurrency_limit=10, # Limit concurrent requests
@@ -675,6 +766,9 @@ def create_app():
675
  max_lines=3,
676
  interactive=False
677
  )
 
 
 
678
 
679
  # Local editing examples
680
  gr.Markdown("### 💡 Local Editing Prompt Examples")
@@ -700,7 +794,7 @@ def create_app():
700
  local_edit_button.click(
701
  fn=local_edit_interface,
702
  inputs=[local_input_image, local_prompt_input],
703
- outputs=[local_output_image, local_status_output],
704
  show_progress=True,
705
  # Increase concurrency settings
706
  concurrency_limit=8, # Local editing is more complex, allow fewer concurrent requests
 
9
 
10
  # Configuration parameters
11
  FREE_TRY_N = 20 # Free phase: first 20 tries without restrictions
12
+ SLOW_TRY_N = 30 # Slow phase start: 30 tries
13
  SLOW2_TRY_N = 40 # Slow phase start: 30 tries
14
+ RATE_LIMIT_60 = 60 # Full restriction: blocked after 60 tries
15
 
16
  # Time window configuration (minutes)
17
  PHASE_1_WINDOW = 5 # 20-30 tries: 3 minutes
 
192
  IP_Dict[client_ip] += 1
193
 
194
  if input_image is None:
195
+ return None, "Please upload an image first", gr.update(visible=False)
196
 
197
  if not prompt or prompt.strip() == "":
198
+ return None, "Please enter editing prompt", gr.update(visible=False)
199
 
200
  # Check if prompt length is greater than 3 characters
201
  if len(prompt.strip()) <= 3:
202
+ return None, "❌ Editing prompt must be more than 3 characters", gr.update(visible=False)
203
  except Exception as e:
204
  print(f"⚠️ Request preprocessing error: {e}")
205
+ return None, "❌ Request processing error", gr.update(visible=False)
206
 
207
  # Get user current phase
208
  current_phase = get_ip_phase(client_ip)
 
212
 
213
  # Check if completely blocked
214
  if current_phase == 'blocked':
215
+ return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation", gr.update(visible=False)
216
 
217
  # Check rate limit (applies to rate_limit phases)
218
  if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']:
219
  is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase)
220
  if is_limited:
221
  wait_minutes_int = int(wait_minutes) + 1
222
+ return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again", gr.update(visible=False)
223
 
224
  # Handle NSFW detection based on phase
225
  is_nsfw_task = False # Track if this task involves NSFW content
 
260
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
261
 
262
  # Call image editing processing function
263
+ result_url, message, task_uuid = process_image_edit(input_image, prompt.strip(), progress_callback)
264
 
265
  if result_url:
266
+ print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
267
 
268
  # Detect result image NSFW content (only in rate limit phases)
269
  if nsfw_detector is not None and current_phase != 'free':
 
314
  except Exception as e:
315
  print(f"⚠️ Final progress update failed: {e}")
316
 
317
+ # Generate action buttons HTML like Trump AI Voice
318
+ action_buttons_html = ""
319
+ if task_uuid:
320
+ task_detail_url = f"https://omnicreator.net/my-creations/task/{task_uuid}"
321
+ action_buttons_html = f"""
322
+ <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
323
+ <a href='{task_detail_url}' target='_blank' style='
324
+ display: inline-flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ padding: 16px 32px;
328
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
329
+ color: white;
330
+ text-decoration: none;
331
+ border-radius: 12px;
332
+ font-weight: 600;
333
+ font-size: 16px;
334
+ text-align: center;
335
+ min-width: 160px;
336
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
337
+ transition: all 0.3s ease;
338
+ border: none;
339
+ '>🖼️ Download HD Image</a>
340
+ <a href='https://omnicreator.net/#generator' target='_blank' style='
341
+ display: inline-flex;
342
+ align-items: center;
343
+ justify-content: center;
344
+ padding: 16px 32px;
345
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
346
+ color: white;
347
+ text-decoration: none;
348
+ border-radius: 12px;
349
+ font-weight: 600;
350
+ font-size: 16px;
351
+ text-align: center;
352
+ min-width: 160px;
353
+ box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
354
+ transition: all 0.3s ease;
355
+ border: none;
356
+ '>🚀 Unlimited Generation</a>
357
+ </div>
358
+ """
359
+
360
+ return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
361
  else:
362
  print(f"❌ Processing failed - IP: {client_ip}, error: {message}", flush=True)
363
+ return None, "❌ " + message, gr.update(visible=False)
364
 
365
  except Exception as e:
366
  print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
367
+ return None, f"❌ Error occurred during processing: {str(e)}", gr.update(visible=False)
368
 
369
  def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Progress()):
370
  """
 
381
  IP_Dict[client_ip] += 1
382
 
383
  if image_dict is None:
384
+ return None, "Please upload an image and draw the area to edit", gr.update(visible=False)
385
 
386
  # Check if background and layers exist
387
  if "background" not in image_dict or "layers" not in image_dict:
388
+ return None, "Please draw the area to edit on the image", gr.update(visible=False)
389
 
390
  base_image = image_dict["background"]
391
  layers = image_dict["layers"]
392
 
393
  if not layers:
394
+ return None, "Please draw the area to edit on the image", gr.update(visible=False)
395
 
396
  if not prompt or prompt.strip() == "":
397
+ return None, "Please enter editing prompt", gr.update(visible=False)
398
 
399
  # Check prompt length
400
  if len(prompt.strip()) <= 3:
401
+ return None, "❌ Editing prompt must be more than 3 characters", gr.update(visible=False)
402
  except Exception as e:
403
  print(f"⚠️ Local edit request preprocessing error: {e}")
404
+ return None, "❌ Request processing error", gr.update(visible=False)
405
 
406
  # Get user current phase
407
  current_phase = get_ip_phase(client_ip)
 
411
 
412
  # Check if completely blocked
413
  if current_phase == 'blocked':
414
+ return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation", gr.update(visible=False)
415
 
416
  # Check rate limit (applies to rate_limit phases)
417
  if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']:
418
  is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase)
419
  if is_limited:
420
  wait_minutes_int = int(wait_minutes) + 1
421
+ return None, f"❌ You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again", gr.update(visible=False)
422
 
423
  # Handle NSFW detection based on phase
424
  is_nsfw_task = False # Track if this task involves NSFW content
 
459
  print(f"✅ Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
460
 
461
  # Call local image editing processing function
462
+ result_url, message, task_uuid = process_local_image_edit(base_image, layers, prompt.strip(), progress_callback)
463
 
464
  if result_url:
465
+ print(f"✅ Local editing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
466
 
467
  # Detect result image NSFW content (only in rate limit phases)
468
  if nsfw_detector is not None and current_phase != 'free':
 
513
  except Exception as e:
514
  print(f"⚠️ Local edit final progress update failed: {e}")
515
 
516
+ # Generate action buttons HTML like Trump AI Voice
517
+ action_buttons_html = ""
518
+ if task_uuid:
519
+ task_detail_url = f"https://omnicreator.net/my-creations/task/{task_uuid}"
520
+ action_buttons_html = f"""
521
+ <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
522
+ <a href='{task_detail_url}' target='_blank' style='
523
+ display: inline-flex;
524
+ align-items: center;
525
+ justify-content: center;
526
+ padding: 16px 32px;
527
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
528
+ color: white;
529
+ text-decoration: none;
530
+ border-radius: 12px;
531
+ font-weight: 600;
532
+ font-size: 16px;
533
+ text-align: center;
534
+ min-width: 160px;
535
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
536
+ transition: all 0.3s ease;
537
+ border: none;
538
+ '>🖼️ Download HD Image</a>
539
+ <a href='https://omnicreator.net/#generator' target='_blank' style='
540
+ display: inline-flex;
541
+ align-items: center;
542
+ justify-content: center;
543
+ padding: 16px 32px;
544
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
545
+ color: white;
546
+ text-decoration: none;
547
+ border-radius: 12px;
548
+ font-weight: 600;
549
+ font-size: 16px;
550
+ text-align: center;
551
+ min-width: 160px;
552
+ box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
553
+ transition: all 0.3s ease;
554
+ border: none;
555
+ '>🚀 Unlimited Generation</a>
556
+ </div>
557
+ """
558
+
559
+ return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
560
  else:
561
  print(f"❌ Local editing processing failed - IP: {client_ip}, error: {message}", flush=True)
562
+ return None, "❌ " + message, gr.update(visible=False)
563
 
564
  except Exception as e:
565
  print(f"❌ Local editing exception - IP: {client_ip}, error: {str(e)}")
566
+ return None, f"❌ Error occurred during processing: {str(e)}", gr.update(visible=False)
567
 
568
  # Create Gradio interface
569
  def create_app():
 
603
  """
604
  ) as app:
605
 
606
+ # Main title - styled like Trump AI Voice
607
+ gr.HTML("""
608
+ <div style="text-align: center; margin: 5px auto 0px auto; max-width: 800px;">
609
+ <h1 style="color: #2c3e50; margin: 0; font-size: 3.5em; font-weight: 800; letter-spacing: 3px; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);">
610
+ 🎨 AI Image Editor
611
+ </h1>
612
+ </div>
613
+ """, padding=False)
614
 
615
+ # Powered by line below title - styled like Trump AI Voice
616
  gr.HTML("""
617
+ <div style="text-align: center; margin: 0px auto -5px auto;">
618
  <p style="margin: 0; font-size: 16px; color: #999; font-weight: 400;">
619
  powered by <a href="https://omnicreator.net/#generator" target="_blank" style="color: #667eea; text-decoration: none;">omnicreator.net</a>
620
  </p>
 
670
  max_lines=3,
671
  interactive=False
672
  )
673
+
674
+ # Action buttons that will show after task completion
675
+ action_buttons = gr.HTML(visible=False)
676
 
677
  # Example area
678
  gr.Markdown("### 💡 Prompt Examples")
 
698
  edit_button.click(
699
  fn=edit_image_interface,
700
  inputs=[input_image, prompt_input],
701
+ outputs=[output_image, status_output, action_buttons],
702
  show_progress=True,
703
  # Increase concurrency settings
704
  concurrency_limit=10, # Limit concurrent requests
 
766
  max_lines=3,
767
  interactive=False
768
  )
769
+
770
+ # Action buttons that will show after task completion
771
+ local_action_buttons = gr.HTML(visible=False)
772
 
773
  # Local editing examples
774
  gr.Markdown("### 💡 Local Editing Prompt Examples")
 
794
  local_edit_button.click(
795
  fn=local_edit_interface,
796
  inputs=[local_input_image, local_prompt_input],
797
+ outputs=[local_output_image, local_status_output, local_action_buttons],
798
  show_progress=True,
799
  # Increase concurrency settings
800
  concurrency_limit=8, # Local editing is more complex, allow fewer concurrent requests
util.py CHANGED
@@ -436,7 +436,7 @@ def process_image_edit(img_input, prompt, progress_callback=None):
436
  # Upload user image directly from memory
437
  uploaded_url = upload_user_img_r2(client_ip, time_id, pil_image)
438
  if not uploaded_url:
439
- return None, "image upload failed"
440
 
441
  # Extract actual image URL from upload URL
442
  if "?" in uploaded_url:
@@ -448,23 +448,28 @@ def process_image_edit(img_input, prompt, progress_callback=None):
448
  # Submit image editing task
449
  task_id, error = submit_image_edit_task(uploaded_url, prompt)
450
  if error:
451
- return None, error
452
 
453
  if progress_callback:
454
  progress_callback(f"task submitted, ID: {task_id}, processing...")
455
 
456
  # Wait for task completion
457
  max_attempts = 60 # Wait up to 10 minutes
 
458
  for attempt in range(max_attempts):
459
  status, output_url, task_data = check_task_status(task_id)
460
 
 
 
 
 
461
  if status == 'completed':
462
  if output_url:
463
- return output_url, "image edit completed"
464
  else:
465
- return None, "Task completed but no result image returned"
466
  elif status == 'error' or status == 'failed':
467
- return None, f"task processing failed: {task_data}"
468
  elif status in ['queued', 'processing', 'running', 'created', 'working']:
469
  if progress_callback:
470
  progress_callback(f"task processing... (status: {status})")
@@ -474,10 +479,10 @@ def process_image_edit(img_input, prompt, progress_callback=None):
474
  progress_callback(f"unknown status: {status}")
475
  time.sleep(1)
476
 
477
- return None, "task processing timeout"
478
 
479
  except Exception as e:
480
- return None, f"error occurred during processing: {str(e)}"
481
 
482
 
483
  def process_local_image_edit(base_image, layers, prompt, progress_callback=None):
@@ -504,7 +509,7 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
504
  # 检查mask是否有内容
505
  mask_array = np.array(mask_image)
506
  if np.max(mask_array) == 0:
507
- return None, "please draw mask"
508
 
509
  print(f"📝 创建mask图片成功,绘制区域像素数: {np.sum(mask_array > 0)}")
510
 
@@ -514,7 +519,7 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
514
  # 直接从内存上传原始图片
515
  uploaded_url = upload_user_img_r2(client_ip, time_id, base_image)
516
  if not uploaded_url:
517
- return None, "original image upload failed"
518
 
519
  # 从上传 URL 中提取实际的图片 URL
520
  if "?" in uploaded_url:
@@ -526,7 +531,7 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
526
  # 直接从内存上传mask图片
527
  mask_url = upload_mask_image_r2(client_ip, time_id, mask_image)
528
  if not mask_url:
529
- return None, "mask image upload failed"
530
 
531
  # 从上传 URL 中提取实际的图片 URL
532
  if "?" in mask_url:
@@ -542,7 +547,7 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
542
  # 提交局部图片编辑任务 (task_type=81)
543
  task_id, error = submit_image_edit_task(uploaded_url, prompt, task_type="81", mask_image_url=mask_url)
544
  if error:
545
- return None, error
546
 
547
  if progress_callback:
548
  progress_callback(f"task submitted, ID: {task_id}, processing...")
@@ -551,17 +556,22 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
551
 
552
  # Wait for task completion
553
  max_attempts = 60 # Wait up to 10 minutes
 
554
  for attempt in range(max_attempts):
555
  status, output_url, task_data = check_task_status(task_id)
556
 
 
 
 
 
557
  if status == 'completed':
558
  if output_url:
559
  print(f"✅ 局部编辑任务完成,结果: {output_url}")
560
- return output_url, "local image edit completed"
561
  else:
562
- return None, "task completed but no result image returned"
563
  elif status == 'error' or status == 'failed':
564
- return None, f"task processing failed: {task_data}"
565
  elif status in ['queued', 'processing', 'running', 'created', 'working']:
566
  if progress_callback:
567
  progress_callback(f"processing... (status: {status})")
@@ -571,11 +581,11 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
571
  progress_callback(f"unknown status: {status}")
572
  time.sleep(1)
573
 
574
- return None, "task processing timeout"
575
 
576
  except Exception as e:
577
  print(f"❌ 局部编辑处理异常: {str(e)}")
578
- return None, f"error occurred during processing: {str(e)}"
579
 
580
 
581
  def download_and_check_result_nsfw(image_url, nsfw_detector=None):
 
436
  # Upload user image directly from memory
437
  uploaded_url = upload_user_img_r2(client_ip, time_id, pil_image)
438
  if not uploaded_url:
439
+ return None, "image upload failed", None
440
 
441
  # Extract actual image URL from upload URL
442
  if "?" in uploaded_url:
 
448
  # Submit image editing task
449
  task_id, error = submit_image_edit_task(uploaded_url, prompt)
450
  if error:
451
+ return None, error, None
452
 
453
  if progress_callback:
454
  progress_callback(f"task submitted, ID: {task_id}, processing...")
455
 
456
  # Wait for task completion
457
  max_attempts = 60 # Wait up to 10 minutes
458
+ task_uuid = None
459
  for attempt in range(max_attempts):
460
  status, output_url, task_data = check_task_status(task_id)
461
 
462
+ # Extract task_uuid from task_data
463
+ if task_data and isinstance(task_data, dict):
464
+ task_uuid = task_data.get('uuid', None)
465
+
466
  if status == 'completed':
467
  if output_url:
468
+ return output_url, "image edit completed", task_uuid
469
  else:
470
+ return None, "Task completed but no result image returned", task_uuid
471
  elif status == 'error' or status == 'failed':
472
+ return None, f"task processing failed: {task_data}", task_uuid
473
  elif status in ['queued', 'processing', 'running', 'created', 'working']:
474
  if progress_callback:
475
  progress_callback(f"task processing... (status: {status})")
 
479
  progress_callback(f"unknown status: {status}")
480
  time.sleep(1)
481
 
482
+ return None, "task processing timeout", task_uuid
483
 
484
  except Exception as e:
485
+ return None, f"error occurred during processing: {str(e)}", None
486
 
487
 
488
  def process_local_image_edit(base_image, layers, prompt, progress_callback=None):
 
509
  # 检查mask是否有内容
510
  mask_array = np.array(mask_image)
511
  if np.max(mask_array) == 0:
512
+ return None, "please draw mask", None
513
 
514
  print(f"📝 创建mask图片成功,绘制区域像素数: {np.sum(mask_array > 0)}")
515
 
 
519
  # 直接从内存上传原始图片
520
  uploaded_url = upload_user_img_r2(client_ip, time_id, base_image)
521
  if not uploaded_url:
522
+ return None, "original image upload failed", None
523
 
524
  # 从上传 URL 中提取实际的图片 URL
525
  if "?" in uploaded_url:
 
531
  # 直接从内存上传mask图片
532
  mask_url = upload_mask_image_r2(client_ip, time_id, mask_image)
533
  if not mask_url:
534
+ return None, "mask image upload failed", None
535
 
536
  # 从上传 URL 中提取实际的图片 URL
537
  if "?" in mask_url:
 
547
  # 提交局部图片编辑任务 (task_type=81)
548
  task_id, error = submit_image_edit_task(uploaded_url, prompt, task_type="81", mask_image_url=mask_url)
549
  if error:
550
+ return None, error, None
551
 
552
  if progress_callback:
553
  progress_callback(f"task submitted, ID: {task_id}, processing...")
 
556
 
557
  # Wait for task completion
558
  max_attempts = 60 # Wait up to 10 minutes
559
+ task_uuid = None
560
  for attempt in range(max_attempts):
561
  status, output_url, task_data = check_task_status(task_id)
562
 
563
+ # Extract task_uuid from task_data
564
+ if task_data and isinstance(task_data, dict):
565
+ task_uuid = task_data.get('uuid', None)
566
+
567
  if status == 'completed':
568
  if output_url:
569
  print(f"✅ 局部编辑任务完成,结果: {output_url}")
570
+ return output_url, "local image edit completed", task_uuid
571
  else:
572
+ return None, "task completed but no result image returned", task_uuid
573
  elif status == 'error' or status == 'failed':
574
+ return None, f"task processing failed: {task_data}", task_uuid
575
  elif status in ['queued', 'processing', 'running', 'created', 'working']:
576
  if progress_callback:
577
  progress_callback(f"processing... (status: {status})")
 
581
  progress_callback(f"unknown status: {status}")
582
  time.sleep(1)
583
 
584
+ return None, "task processing timeout", task_uuid
585
 
586
  except Exception as e:
587
  print(f"❌ 局部编辑处理异常: {str(e)}")
588
+ return None, f"error occurred during processing: {str(e)}", None
589
 
590
 
591
  def download_and_check_result_nsfw(image_url, nsfw_detector=None):