Spaces:
Running
Running
| import gradio as gr | |
| import threading | |
| import os | |
| import shutil | |
| import tempfile | |
| import time | |
| from util import process_multi_image_edit, process_local_image_edit, download_and_check_result_nsfw | |
| from nfsw import NSFWDetector | |
| # Configuration parameters | |
| FREE_TRY_N = 20 # Free phase: first 20 tries without restrictions | |
| SLOW_TRY_N = 30 # Slow phase start: 30 tries | |
| SLOW2_TRY_N = 40 # Slow phase start: 30 tries | |
| RATE_LIMIT_60 = 60 # Full restriction: blocked after 60 tries | |
| # Time window configuration (minutes) | |
| PHASE_1_WINDOW = 5 # 20-30 tries: 3 minutes | |
| PHASE_2_WINDOW = 8 # 30-40 tries: 6 minutes | |
| PHASE_3_WINDOW = 15 # 40-60 tries: 10 minutes | |
| MAX_IMAGES_PER_WINDOW = 2 # Max images per time window | |
| IP_Dict = {} | |
| # IP generation statistics and time window tracking | |
| IP_Generation_Count = {} # Record total generation count for each IP | |
| IP_Rate_Limit_Track = {} # Record generation count and timestamp in current time window for each IP | |
| def get_ip_generation_count(client_ip): | |
| """ | |
| Get IP generation count | |
| """ | |
| if client_ip not in IP_Generation_Count: | |
| IP_Generation_Count[client_ip] = 0 | |
| return IP_Generation_Count[client_ip] | |
| def increment_ip_generation_count(client_ip): | |
| """ | |
| Increment IP generation count | |
| """ | |
| if client_ip not in IP_Generation_Count: | |
| IP_Generation_Count[client_ip] = 0 | |
| IP_Generation_Count[client_ip] += 1 | |
| return IP_Generation_Count[client_ip] | |
| def get_ip_phase(client_ip): | |
| """ | |
| Get current phase for IP | |
| Returns: | |
| str: 'free', 'rate_limit_1', 'rate_limit_2', 'rate_limit_3', 'blocked' | |
| """ | |
| count = get_ip_generation_count(client_ip) | |
| if count < FREE_TRY_N: # 0-19 tries | |
| return 'free' | |
| elif count < SLOW_TRY_N: # 20-29 tries | |
| return 'rate_limit_1' # NSFW blur + 3 minutes 2 images | |
| elif count < SLOW2_TRY_N: # 30-39 tries | |
| return 'rate_limit_2' # NSFW blur + 6 minutes 2 images | |
| elif count < RATE_LIMIT_60: # 40-59 tries | |
| return 'rate_limit_3' # NSFW blur + 10 minutes 2 images | |
| else: # 60+ tries | |
| return 'blocked' # Generation blocked | |
| def check_rate_limit_for_phase(client_ip, phase): | |
| """ | |
| Check rate limit for specific phase | |
| Returns: | |
| tuple: (is_limited, wait_time_minutes, current_count) | |
| """ | |
| if phase not in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']: | |
| return False, 0, 0 | |
| # Determine time window | |
| if phase == 'rate_limit_1': | |
| window_minutes = PHASE_1_WINDOW # 3 minutes | |
| elif phase == 'rate_limit_2': | |
| window_minutes = PHASE_2_WINDOW # 6 minutes | |
| else: # rate_limit_3 | |
| window_minutes = PHASE_3_WINDOW # 10 minutes | |
| current_time = time.time() | |
| window_key = f"{client_ip}_{phase}" | |
| # Clean expired records | |
| if window_key in IP_Rate_Limit_Track: | |
| track_data = IP_Rate_Limit_Track[window_key] | |
| # Check if within current time window | |
| if current_time - track_data['start_time'] > window_minutes * 60: | |
| # Time window expired, reset | |
| IP_Rate_Limit_Track[window_key] = { | |
| 'count': 0, | |
| 'start_time': current_time, | |
| 'last_generation': current_time | |
| } | |
| else: | |
| # Initialize | |
| IP_Rate_Limit_Track[window_key] = { | |
| 'count': 0, | |
| 'start_time': current_time, | |
| 'last_generation': current_time | |
| } | |
| track_data = IP_Rate_Limit_Track[window_key] | |
| # Check if exceeded limit | |
| if track_data['count'] >= MAX_IMAGES_PER_WINDOW: | |
| # Calculate remaining wait time | |
| elapsed = current_time - track_data['start_time'] | |
| wait_time = (window_minutes * 60) - elapsed | |
| wait_minutes = max(0, wait_time / 60) | |
| return True, wait_minutes, track_data['count'] | |
| return False, 0, track_data['count'] | |
| def record_generation_attempt(client_ip, phase): | |
| """ | |
| Record generation attempt | |
| """ | |
| # Increment total count | |
| increment_ip_generation_count(client_ip) | |
| # Record time window count | |
| if phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']: | |
| window_key = f"{client_ip}_{phase}" | |
| current_time = time.time() | |
| if window_key in IP_Rate_Limit_Track: | |
| IP_Rate_Limit_Track[window_key]['count'] += 1 | |
| IP_Rate_Limit_Track[window_key]['last_generation'] = current_time | |
| else: | |
| IP_Rate_Limit_Track[window_key] = { | |
| 'count': 1, | |
| 'start_time': current_time, | |
| 'last_generation': current_time | |
| } | |
| def apply_gaussian_blur_to_image_url(image_url, blur_strength=50): | |
| """ | |
| Apply Gaussian blur to image URL | |
| Args: | |
| image_url (str): Original image URL | |
| blur_strength (int): Blur strength, default 50 (heavy blur) | |
| Returns: | |
| PIL.Image: Blurred PIL Image object | |
| """ | |
| try: | |
| import requests | |
| from PIL import Image, ImageFilter | |
| import io | |
| # Download image | |
| response = requests.get(image_url, timeout=30) | |
| if response.status_code != 200: | |
| return None | |
| # Convert to PIL Image | |
| image_data = io.BytesIO(response.content) | |
| image = Image.open(image_data) | |
| # Apply heavy Gaussian blur | |
| blurred_image = image.filter(ImageFilter.GaussianBlur(radius=blur_strength)) | |
| return blurred_image | |
| except Exception as e: | |
| print(f"β οΈ Failed to apply Gaussian blur: {e}") | |
| return None | |
| # Initialize NSFW detector (download from Hugging Face) | |
| try: | |
| nsfw_detector = NSFWDetector() # Auto download falconsai_yolov9_nsfw_model_quantized.pt from Hugging Face | |
| print("β NSFW detector initialized successfully") | |
| except Exception as e: | |
| print(f"β NSFW detector initialization failed: {e}") | |
| nsfw_detector = None | |
| def edit_image_interface(input_image1, input_image2, input_image3, prompt, aspect_ratio, request: gr.Request, progress=gr.Progress()): | |
| """ | |
| Interface function for processing multi-image editing with phase-based limitations | |
| """ | |
| try: | |
| # Extract user IP | |
| client_ip = request.client.host | |
| x_forwarded_for = dict(request.headers).get('x-forwarded-for') | |
| if x_forwarded_for: | |
| client_ip = x_forwarded_for | |
| if client_ip not in IP_Dict: | |
| IP_Dict[client_ip] = 0 | |
| IP_Dict[client_ip] += 1 | |
| # Validate input images | |
| input_images = [input_image1, input_image2, input_image3] | |
| valid_images = [img for img in input_images if img is not None] | |
| if len(valid_images) < 2: | |
| return None, "Please upload at least 2 images", gr.update(visible=False) | |
| if len(valid_images) > 3: | |
| return None, "Maximum 3 images allowed", gr.update(visible=False) | |
| if not prompt or prompt.strip() == "": | |
| return None, "Please enter editing prompt", gr.update(visible=False) | |
| # Check if prompt length is greater than 3 characters | |
| if len(prompt.strip()) <= 3: | |
| return None, "β Editing prompt must be more than 3 characters", gr.update(visible=False) | |
| except Exception as e: | |
| print(f"β οΈ Request preprocessing error: {e}") | |
| return None, "β Request processing error", gr.update(visible=False) | |
| # Get user current phase | |
| current_phase = get_ip_phase(client_ip) | |
| current_count = get_ip_generation_count(client_ip) | |
| print(f"π User phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}") | |
| # Check if completely blocked | |
| if current_phase == 'blocked': | |
| # Generate blocked limit button | |
| blocked_button_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π Unlimited Generation</a> | |
| </div> | |
| """ | |
| return None, f"β You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/multi-image-edit#generator for unlimited generation", gr.update(value=blocked_button_html, visible=True) | |
| # Check rate limit (applies to rate_limit phases) | |
| if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']: | |
| is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase) | |
| if is_limited: | |
| wait_minutes_int = int(wait_minutes) + 1 | |
| # Generate rate limit button | |
| rate_limit_button_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(243, 156, 18, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>β° Skip Wait - Unlimited Generation</a> | |
| </div> | |
| """ | |
| return None, f"β You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/multi-image-edit#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again", gr.update(value=rate_limit_button_html, visible=True) | |
| # Parse aspect ratio to get width and height | |
| width, height = 0, 0 # Default to auto sizing | |
| if aspect_ratio and aspect_ratio != "Auto": | |
| aspect_ratios = { | |
| "16:9": (1364, 768), | |
| "4:3": (1182, 887), | |
| "1:1": (1024, 1024), | |
| "3:4": (887, 1182), | |
| "9:16": (768, 1364) | |
| } | |
| if aspect_ratio in aspect_ratios: | |
| width, height = aspect_ratios[aspect_ratio] | |
| # Handle NSFW detection based on phase | |
| is_nsfw_task = False # Track if this task involves NSFW content | |
| # Skip NSFW detection in free phase - check first image for NSFW | |
| if current_phase != 'free' and nsfw_detector is not None and valid_images: | |
| try: | |
| nsfw_result = nsfw_detector.predict_pil_label_only(valid_images[0]) | |
| if nsfw_result.lower() == "nsfw": | |
| is_nsfw_task = True | |
| print(f"π Input NSFW detected in {current_phase} phase: βββ {nsfw_result} - IP: {client_ip} (will blur result)") | |
| else: | |
| print(f"π Input NSFW check passed: β β β {nsfw_result} - IP: {client_ip}") | |
| except Exception as e: | |
| print(f"β οΈ Input NSFW detection failed: {e}") | |
| # Allow continuation when detection fails | |
| result_url = None | |
| status_message = "" | |
| def progress_callback(message): | |
| try: | |
| nonlocal status_message | |
| status_message = message | |
| # Add error handling to prevent progress update failure | |
| if progress is not None: | |
| progress(0.5, desc=message) | |
| except Exception as e: | |
| print(f"β οΈ Progress update failed: {e}") | |
| try: | |
| # Record generation attempt (before actual generation to ensure correct count) | |
| record_generation_attempt(client_ip, current_phase) | |
| updated_count = get_ip_generation_count(client_ip) | |
| print(f"β Multi-image processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, images: {len(valid_images)}, size: {width}x{height}, prompt: {prompt.strip()}", flush=True) | |
| # Call multi-image editing processing function | |
| result_url, message, task_uuid = process_multi_image_edit(valid_images, prompt.strip(), width, height, progress_callback) | |
| if result_url: | |
| print(f"β Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True) | |
| # Detect result image NSFW content (only in rate limit phases) | |
| if nsfw_detector is not None and current_phase != 'free': | |
| try: | |
| if progress is not None: | |
| progress(0.9, desc="Checking result image...") | |
| is_nsfw, nsfw_error = download_and_check_result_nsfw(result_url, nsfw_detector) | |
| if nsfw_error: | |
| print(f"β οΈ Result image NSFW detection error - IP: {client_ip}, error: {nsfw_error}") | |
| elif is_nsfw: | |
| is_nsfw_task = True # Mark task as NSFW | |
| print(f"π Result image NSFW detected in {current_phase} phase: βββ - IP: {client_ip} (will blur result)") | |
| else: | |
| print(f"π Result image NSFW check passed: β β β - IP: {client_ip}") | |
| except Exception as e: | |
| print(f"β οΈ Result image NSFW detection exception - IP: {client_ip}, error: {str(e)}") | |
| # Apply blur if this is an NSFW task in rate limit phases | |
| should_blur = False | |
| if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3'] and is_nsfw_task: | |
| should_blur = True | |
| # Apply blur processing | |
| if should_blur: | |
| if progress is not None: | |
| progress(0.95, desc="Applying content filter...") | |
| blurred_image = apply_gaussian_blur_to_image_url(result_url) | |
| if blurred_image is not None: | |
| final_result = blurred_image # Return PIL Image object | |
| final_message = f"β οΈ NSFW content detected, content filter applied. NSFW content is prohibited by Hugging Face, but you can generate unlimited content at our official website https://omnicreator.net/multi-image-edit#generator" | |
| print(f"π Applied Gaussian blur for NSFW content - IP: {client_ip}") | |
| else: | |
| # Blur failed, return original URL with warning | |
| final_result = result_url | |
| final_message = f"β οΈ NSFW content detected, but content filter failed. Please visit https://omnicreator.net/multi-image-edit#generator for better experience" | |
| # Generate NSFW button for blurred content | |
| nsfw_action_buttons_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π₯ Unlimited NSFW Generation</a> | |
| </div> | |
| """ | |
| return final_result, final_message, gr.update(value=nsfw_action_buttons_html, visible=True) | |
| else: | |
| final_result = result_url | |
| final_message = "β " + message | |
| try: | |
| if progress is not None: | |
| progress(1.0, desc="Processing completed") | |
| except Exception as e: | |
| print(f"β οΈ Final progress update failed: {e}") | |
| # Generate action buttons HTML like Trump AI Voice | |
| action_buttons_html = "" | |
| if task_uuid: | |
| task_detail_url = f"https://omnicreator.net/my-creations/task/{task_uuid}" | |
| action_buttons_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='{task_detail_url}' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 160px; | |
| box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>πΌοΈ Download HD Image</a> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 160px; | |
| box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π Unlimited Generation</a> | |
| </div> | |
| """ | |
| return final_result, final_message, gr.update(value=action_buttons_html, visible=True) | |
| else: | |
| print(f"β Processing failed - IP: {client_ip}, error: {message}", flush=True) | |
| return None, "β " + message, gr.update(visible=False) | |
| except Exception as e: | |
| print(f"β Processing exception - IP: {client_ip}, error: {str(e)}") | |
| return None, f"β Error occurred during processing: {str(e)}", gr.update(visible=False) | |
| def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Progress()): | |
| """ | |
| Handle local editing requests (with phase-based limitations) | |
| """ | |
| try: | |
| # Extract user IP | |
| client_ip = request.client.host | |
| x_forwarded_for = dict(request.headers).get('x-forwarded-for') | |
| if x_forwarded_for: | |
| client_ip = x_forwarded_for | |
| if client_ip not in IP_Dict: | |
| IP_Dict[client_ip] = 0 | |
| IP_Dict[client_ip] += 1 | |
| if image_dict is None: | |
| return None, "Please upload an image and draw the area to edit", gr.update(visible=False) | |
| # Check if background and layers exist | |
| if "background" not in image_dict or "layers" not in image_dict: | |
| return None, "Please draw the area to edit on the image", gr.update(visible=False) | |
| base_image = image_dict["background"] | |
| layers = image_dict["layers"] | |
| if not layers: | |
| return None, "Please draw the area to edit on the image", gr.update(visible=False) | |
| if not prompt or prompt.strip() == "": | |
| return None, "Please enter editing prompt", gr.update(visible=False) | |
| # Check prompt length | |
| if len(prompt.strip()) <= 3: | |
| return None, "β Editing prompt must be more than 3 characters", gr.update(visible=False) | |
| except Exception as e: | |
| print(f"β οΈ Local edit request preprocessing error: {e}") | |
| return None, "β Request processing error", gr.update(visible=False) | |
| # Get user current phase | |
| current_phase = get_ip_phase(client_ip) | |
| current_count = get_ip_generation_count(client_ip) | |
| print(f"π Local edit user phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}") | |
| # Check if completely blocked | |
| if current_phase == 'blocked': | |
| # Generate blocked limit button | |
| blocked_button_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π Unlimited Generation</a> | |
| </div> | |
| """ | |
| return None, f"β You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/multi-image-edit#generator for unlimited generation", gr.update(value=blocked_button_html, visible=True) | |
| # Check rate limit (applies to rate_limit phases) | |
| if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']: | |
| is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase) | |
| if is_limited: | |
| wait_minutes_int = int(wait_minutes) + 1 | |
| # Generate rate limit button | |
| rate_limit_button_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(243, 156, 18, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>β° Skip Wait - Unlimited Generation</a> | |
| </div> | |
| """ | |
| return None, f"β You have reached Hugging Face's free generation limit. Please visit https://omnicreator.net/multi-image-edit#generator for unlimited generation, or wait {wait_minutes_int} minutes before generating again", gr.update(value=rate_limit_button_html, visible=True) | |
| # Handle NSFW detection based on phase | |
| is_nsfw_task = False # Track if this task involves NSFW content | |
| # Skip NSFW detection in free phase | |
| if current_phase != 'free' and nsfw_detector is not None and base_image is not None: | |
| try: | |
| nsfw_result = nsfw_detector.predict_pil_label_only(base_image) | |
| if nsfw_result.lower() == "nsfw": | |
| is_nsfw_task = True | |
| print(f"π Local edit input NSFW detected in {current_phase} phase: βββ {nsfw_result} - IP: {client_ip} (will blur result)") | |
| else: | |
| print(f"π Local edit input NSFW check passed: β β β {nsfw_result} - IP: {client_ip}") | |
| except Exception as e: | |
| print(f"β οΈ Local edit input NSFW detection failed: {e}") | |
| # Allow continuation when detection fails | |
| result_url = None | |
| status_message = "" | |
| def progress_callback(message): | |
| try: | |
| nonlocal status_message | |
| status_message = message | |
| # Add error handling to prevent progress update failure | |
| if progress is not None: | |
| progress(0.5, desc=message) | |
| except Exception as e: | |
| print(f"β οΈ Local edit progress update failed: {e}") | |
| try: | |
| # Record generation attempt (before actual generation to ensure correct count) | |
| record_generation_attempt(client_ip, current_phase) | |
| updated_count = get_ip_generation_count(client_ip) | |
| print(f"β Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True) | |
| # Call local image editing processing function | |
| result_url, message, task_uuid = process_local_image_edit(base_image, layers, prompt.strip(), progress_callback) | |
| if result_url: | |
| print(f"β Local editing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True) | |
| # Detect result image NSFW content (only in rate limit phases) | |
| if nsfw_detector is not None and current_phase != 'free': | |
| try: | |
| if progress is not None: | |
| progress(0.9, desc="Checking result image...") | |
| is_nsfw, nsfw_error = download_and_check_result_nsfw(result_url, nsfw_detector) | |
| if nsfw_error: | |
| print(f"β οΈ Local edit result image NSFW detection error - IP: {client_ip}, error: {nsfw_error}") | |
| elif is_nsfw: | |
| is_nsfw_task = True # Mark task as NSFW | |
| print(f"π Local edit result image NSFW detected in {current_phase} phase: βββ - IP: {client_ip} (will blur result)") | |
| else: | |
| print(f"π Local edit result image NSFW check passed: β β β - IP: {client_ip}") | |
| except Exception as e: | |
| print(f"β οΈ Local edit result image NSFW detection exception - IP: {client_ip}, error: {str(e)}") | |
| # Apply blur if this is an NSFW task in rate limit phases | |
| should_blur = False | |
| if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3'] and is_nsfw_task: | |
| should_blur = True | |
| # Apply blur processing | |
| if should_blur: | |
| if progress is not None: | |
| progress(0.95, desc="Applying content filter...") | |
| blurred_image = apply_gaussian_blur_to_image_url(result_url) | |
| if blurred_image is not None: | |
| final_result = blurred_image # Return PIL Image object | |
| final_message = f"β οΈ NSFW content detected, content filter applied. NSFW content is prohibited by Hugging Face, but you can generate unlimited content at our official website https://omnicreator.net/multi-image-edit#generator" | |
| print(f"π Local edit applied Gaussian blur for NSFW content - IP: {client_ip}") | |
| else: | |
| # Blur failed, return original URL with warning | |
| final_result = result_url | |
| final_message = f"β οΈ NSFW content detected, but content filter failed. Please visit https://omnicreator.net/multi-image-edit#generator for better experience" | |
| # Generate NSFW button for blurred content | |
| nsfw_action_buttons_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π₯ Unlimited NSFW Generation</a> | |
| </div> | |
| """ | |
| return final_result, final_message, gr.update(value=nsfw_action_buttons_html, visible=True) | |
| else: | |
| final_result = result_url | |
| final_message = "β " + message | |
| try: | |
| if progress is not None: | |
| progress(1.0, desc="Processing completed") | |
| except Exception as e: | |
| print(f"β οΈ Local edit final progress update failed: {e}") | |
| # Generate action buttons HTML like Trump AI Voice | |
| action_buttons_html = "" | |
| if task_uuid: | |
| task_detail_url = f"https://omnicreator.net/my-creations/task/{task_uuid}" | |
| action_buttons_html = f""" | |
| <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'> | |
| <a href='{task_detail_url}' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 160px; | |
| box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>πΌοΈ Download HD Image</a> | |
| <a href='https://omnicreator.net/multi-image-edit#generator' target='_blank' style=' | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 16px 32px; | |
| background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 12px; | |
| font-weight: 600; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 160px; | |
| box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| '>π Unlimited Generation</a> | |
| </div> | |
| """ | |
| return final_result, final_message, gr.update(value=action_buttons_html, visible=True) | |
| else: | |
| print(f"β Local editing processing failed - IP: {client_ip}, error: {message}", flush=True) | |
| return None, "β " + message, gr.update(visible=False) | |
| except Exception as e: | |
| print(f"β Local editing exception - IP: {client_ip}, error: {str(e)}") | |
| return None, f"β Error occurred during processing: {str(e)}", gr.update(visible=False) | |
| # Create Gradio interface | |
| def create_app(): | |
| with gr.Blocks( | |
| title="AI Image Editor", | |
| theme=gr.themes.Soft(), | |
| css=""" | |
| .main-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .upload-area { | |
| border: 2px dashed #ccc; | |
| border-radius: 10px; | |
| padding: 20px; | |
| text-align: center; | |
| } | |
| .result-area { | |
| margin-top: 20px; | |
| padding: 20px; | |
| border-radius: 10px; | |
| background-color: #f8f9fa; | |
| } | |
| .use-as-input-btn { | |
| margin-top: 10px; | |
| width: 100%; | |
| } | |
| """, | |
| # Improve concurrency performance configuration | |
| head=""" | |
| <script> | |
| // Reduce client-side state update frequency, avoid excessive SSE connections | |
| if (window.gradio) { | |
| window.gradio.update_frequency = 2000; // Update every 2 seconds | |
| } | |
| </script> | |
| """ | |
| ) as app: | |
| # Main title - styled like Trump AI Voice | |
| gr.HTML(""" | |
| <div style="text-align: center; margin: 5px auto 0px auto; max-width: 800px;"> | |
| <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);"> | |
| π¨ AI Multi-Image Editor | |
| </h1> | |
| </div> | |
| """, padding=False) | |
| # Powered by line below title - styled like Trump AI Voice | |
| gr.HTML(""" | |
| <div style="text-align: center; margin: 0px auto -5px auto;"> | |
| <p style="margin: 0; font-size: 16px; color: #999; font-weight: 400;"> | |
| powered by <a href="https://omnicreator.net/multi-image-edit#generator" target="_blank" style="color: #667eea; text-decoration: none;">omnicreator.net</a> | |
| </p> | |
| </div> | |
| """, padding=False) | |
| with gr.Tabs(): | |
| # Multi-image editing tab | |
| with gr.Tab("πΌοΈ Multi-Image Editing"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### πΈ Upload Images (2-3 images)") | |
| # Multiple image inputs | |
| with gr.Row(): | |
| input_image1 = gr.Image( | |
| label="Image 1 *", | |
| type="pil", | |
| height=200, | |
| elem_classes=["upload-area"] | |
| ) | |
| input_image2 = gr.Image( | |
| label="Image 2 *", | |
| type="pil", | |
| height=200, | |
| elem_classes=["upload-area"] | |
| ) | |
| input_image3 = gr.Image( | |
| label="Image 3 (optional)", | |
| type="pil", | |
| height=200, | |
| elem_classes=["upload-area"] | |
| ) | |
| gr.Markdown("### π Output Size") | |
| aspect_ratio_selector = gr.Radio( | |
| choices=["Auto", "16:9", "4:3", "1:1", "3:4", "9:16"], | |
| value="Auto", | |
| label="Select aspect ratio", | |
| info="Choose output dimensions or Auto for original sizing" | |
| ) | |
| gr.Markdown("### βοΈ Multi-Image Editing Instructions") | |
| prompt_input = gr.Textbox( | |
| label="Enter multi-image editing prompt", | |
| placeholder="For example: combine these images into a single scene, merge the best parts from each image, create a collage with artistic transitions...", | |
| lines=4, | |
| max_lines=6 | |
| ) | |
| edit_button = gr.Button( | |
| "π Start Multi-Image Editing", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π― Multi-Image Editing Result") | |
| output_image = gr.Image( | |
| label="Multi-image edited result", | |
| height=400, | |
| elem_classes=["result-area"] | |
| ) | |
| # Add "Use as Input" button | |
| use_as_input_btn = gr.Button( | |
| "π Use as Input", | |
| variant="secondary", | |
| size="sm", | |
| elem_classes=["use-as-input-btn"] | |
| ) | |
| status_output = gr.Textbox( | |
| label="Processing status", | |
| lines=2, | |
| max_lines=3, | |
| interactive=False | |
| ) | |
| # Action buttons that will show after task completion | |
| action_buttons = gr.HTML(visible=False) | |
| # Example area | |
| gr.Markdown("### π‘ Multi-Image Editing Examples") | |
| # Helper function to load example images | |
| def load_example_1(): | |
| """Load dancing cats example""" | |
| try: | |
| from PIL import Image | |
| img1 = Image.open("datas/data01/tom02.webp") | |
| img2 = Image.open("datas/data01/tom.webp") | |
| return img1, img2, None, "Let the 2 cats dance together", "16:9" | |
| except Exception as e: | |
| print(f"Failed to load example 1 images: {e}") | |
| return None, None, None, "Let the 2 cats dance together", "16:9" | |
| def load_example_2(): | |
| """Load fashion try-on example""" | |
| try: | |
| from PIL import Image | |
| img1 = Image.open("datas/data02/girl.jpg") | |
| img2 = Image.open("datas/data02/cloth.jpeg") | |
| return img1, img2, None, "Let the girl in first image, wear the dress in second image", "9:16" | |
| except Exception as e: | |
| print(f"Failed to load example 2 images: {e}") | |
| return None, None, None, "Let the girl in first image, wear the dress in second image", "9:16" | |
| def load_example_3(): | |
| """Load beach bikini example""" | |
| try: | |
| from PIL import Image | |
| img1 = Image.open("datas/data03/girl.webp") | |
| img2 = Image.open("datas/data03/cloth.jpg") | |
| return img1, img2, None, "Let the girl in first image, wear the bikini in second image, lying on the beach", "1:1" | |
| except Exception as e: | |
| print(f"Failed to load example 3 images: {e}") | |
| return None, None, None, "Let the girl in first image, wear the bikini in second image, lying on the beach", "1:1" | |
| # Example 1: Cats dancing | |
| gr.Markdown("#### π± Example 1: Dancing Cats (2 images)") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Preview images for example 1 | |
| with gr.Row(): | |
| try: | |
| gr.Image("datas/data01/tom02.webp", label="Cat 1", height=100, width=100, show_label=False, interactive=False) | |
| gr.Image("datas/data01/tom.webp", label="Cat 2", height=100, width=100, show_label=False, interactive=False) | |
| except: | |
| gr.Markdown("*Preview images not available*") | |
| gr.Markdown("**Prompt**: Let the 2 cats dance together \n**Size**: 16:9") | |
| with gr.Column(scale=1): | |
| gr.Button( | |
| "π Load Dancing Cats Example", | |
| size="lg", | |
| variant="secondary" | |
| ).click( | |
| fn=load_example_1, | |
| outputs=[input_image1, input_image2, input_image3, prompt_input, aspect_ratio_selector] | |
| ) | |
| # Example 2: Girl wearing dress | |
| gr.Markdown("#### π Example 2: Fashion Try-on (2 images)") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Preview images for example 2 | |
| with gr.Row(): | |
| try: | |
| gr.Image("datas/data02/girl.jpg", label="Girl", height=100, width=100, show_label=False, interactive=False) | |
| gr.Image("datas/data02/cloth.jpeg", label="Dress", height=100, width=100, show_label=False, interactive=False) | |
| except: | |
| gr.Markdown("*Preview images not available*") | |
| gr.Markdown("**Prompt**: Let the girl in first image, wear the dress in second image \n**Size**: 9:16") | |
| with gr.Column(scale=1): | |
| gr.Button( | |
| "π Load Fashion Try-on Example", | |
| size="lg", | |
| variant="secondary" | |
| ).click( | |
| fn=load_example_2, | |
| outputs=[input_image1, input_image2, input_image3, prompt_input, aspect_ratio_selector] | |
| ) | |
| # Example 3: Beach bikini | |
| gr.Markdown("#### ποΈ Example 3: Beach Style (2 images)") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Preview images for example 3 | |
| with gr.Row(): | |
| try: | |
| gr.Image("datas/data03/girl.webp", label="Girl", height=100, width=100, show_label=False, interactive=False) | |
| gr.Image("datas/data03/cloth.jpg", label="Bikini", height=100, width=100, show_label=False, interactive=False) | |
| except: | |
| gr.Markdown("*Preview images not available*") | |
| gr.Markdown("**Prompt**: Let the girl in first image, wear the bikini in second image, lying on the beach \n**Size**: 1:1") | |
| with gr.Column(scale=1): | |
| gr.Button( | |
| "ποΈ Load Beach Style Example", | |
| size="lg", | |
| variant="secondary" | |
| ).click( | |
| fn=load_example_3, | |
| outputs=[input_image1, input_image2, input_image3, prompt_input, aspect_ratio_selector] | |
| ) | |
| # Additional quick prompt examples | |
| gr.Markdown("#### β¨ Quick Prompts") | |
| with gr.Row(): | |
| additional_prompts = [ | |
| "Combine these images into a single artistic scene", | |
| "Create a collage with artistic transitions between images", | |
| "Blend these images into a cohesive composition" | |
| ] | |
| for prompt in additional_prompts: | |
| gr.Button( | |
| prompt, | |
| size="sm" | |
| ).click( | |
| lambda p=prompt: p, | |
| outputs=prompt_input | |
| ) | |
| # Bind button click events - simplified, remove state management | |
| edit_button.click( | |
| fn=edit_image_interface, | |
| inputs=[input_image1, input_image2, input_image3, prompt_input, aspect_ratio_selector], | |
| outputs=[output_image, status_output, action_buttons], | |
| show_progress=True, | |
| # Increase concurrency settings | |
| concurrency_limit=8, # Limit concurrent requests for multi-image processing | |
| api_name="multi_image_edit" | |
| ) | |
| # Simplify "Use as Input" button, use result as first image | |
| def simple_use_as_input(output_img): | |
| if output_img is not None: | |
| return output_img, None, None # Set as first image, clear others | |
| return None, None, None | |
| use_as_input_btn.click( | |
| fn=simple_use_as_input, | |
| inputs=[output_image], | |
| outputs=[input_image1, input_image2, input_image3] | |
| ) | |
| # # Local editing tab | |
| # with gr.Tab("ποΈ Local Editing"): | |
| # with gr.Row(): | |
| # with gr.Column(scale=1): | |
| # gr.Markdown("### πΈ Upload Image and Draw Edit Area") | |
| # local_input_image = gr.ImageEditor( | |
| # label="Upload image and draw mask", | |
| # type="pil", | |
| # height=512, | |
| # brush=gr.Brush(colors=["#ff0000"], default_size=180), | |
| # elem_classes=["upload-area"] | |
| # ) | |
| # gr.Markdown("### βοΈ Editing Instructions") | |
| # local_prompt_input = gr.Textbox( | |
| # label="Enter local editing prompt", | |
| # placeholder="For example: change selected area hair to golden, add patterns to selected object, change selected area color, etc...", | |
| # lines=3, | |
| # max_lines=5 | |
| # ) | |
| # local_edit_button = gr.Button( | |
| # "π― Start Local Editing", | |
| # variant="primary", | |
| # size="lg" | |
| # ) | |
| # with gr.Column(scale=1): | |
| # gr.Markdown("### π― Editing Result") | |
| # local_output_image = gr.Image( | |
| # label="Local edited image", | |
| # height=320, | |
| # elem_classes=["result-area"] | |
| # ) | |
| # # Add "Use as Input" button | |
| # local_use_as_input_btn = gr.Button( | |
| # "π Use as Input", | |
| # variant="secondary", | |
| # size="sm", | |
| # elem_classes=["use-as-input-btn"] | |
| # ) | |
| # local_status_output = gr.Textbox( | |
| # label="Processing status", | |
| # lines=2, | |
| # max_lines=3, | |
| # interactive=False | |
| # ) | |
| # Action buttons that will show after task completion | |
| # local_action_buttons = gr.HTML(visible=False) | |
| # # Local editing examples | |
| # gr.Markdown("### π‘ Local Editing Prompt Examples") | |
| # with gr.Row(): | |
| # local_example_prompts = [ | |
| # "Change selected area hair to golden", | |
| # "Add pattern designs to selected clothing", | |
| # "Change selected area to different material", | |
| # "Add decorations to selected object", | |
| # "Change selected area color and style" | |
| # ] | |
| # for prompt in local_example_prompts: | |
| # gr.Button( | |
| # prompt, | |
| # size="sm" | |
| # ).click( | |
| # lambda p=prompt: p, | |
| # outputs=local_prompt_input | |
| # ) | |
| # # Bind local edit button click events - simplified, remove state management | |
| # local_edit_button.click( | |
| # fn=local_edit_interface, | |
| # inputs=[local_input_image, local_prompt_input], | |
| # outputs=[local_output_image, local_status_output, local_action_buttons], | |
| # show_progress=True, | |
| # # Increase concurrency settings | |
| # concurrency_limit=8, # Local editing is more complex, allow fewer concurrent requests | |
| # api_name="local_edit" | |
| # ) | |
| # # Simplify local edit "Use as Input" button | |
| # def simple_local_use_as_input(output_img): | |
| # if output_img is not None: | |
| # # Create simple ImageEditor format | |
| # editor_data = { | |
| # "background": output_img, | |
| # "layers": [], | |
| # "composite": output_img | |
| # } | |
| # return editor_data | |
| # return None | |
| # local_use_as_input_btn.click( | |
| # fn=simple_local_use_as_input, | |
| # inputs=[local_output_image], | |
| # outputs=[local_input_image] | |
| # ) | |
| # SEO Content Section | |
| gr.HTML(""" | |
| <div style="width: 100%; margin: 50px 0; padding: 0 20px;"> | |
| <!-- Showcase Section --> | |
| <div style="text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px; border-radius: 20px; margin: 40px 0;"> | |
| <h2 style="margin: 0 0 20px 0; font-size: 2.2em; font-weight: 700;"> | |
| π¨ Unlimited AI Image Generation & Editing | |
| </h2> | |
| <p style="margin: 0 0 25px 0; font-size: 1.2em; opacity: 0.95; line-height: 1.6;"> | |
| Experience the ultimate freedom in AI image creation! Generate and edit unlimited images without restrictions, | |
| including NSFW content, with our premium AI image editing platform. | |
| </p> | |
| <div style="display: flex; justify-content: center; gap: 25px; flex-wrap: wrap; margin: 30px 0;"> | |
| <a href="https://omnicreator.net/multi-image-edit#generator" target="_blank" style=" | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px 40px; | |
| background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 15px; | |
| font-weight: 700; | |
| font-size: 18px; | |
| text-align: center; | |
| min-width: 250px; | |
| box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| transform: scale(1); | |
| " onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'"> | |
| π Get Unlimited Access Now | |
| </a> | |
| </div> | |
| <p style="color: rgba(255,255,255,0.9); font-size: 1em; margin: 20px 0 0 0;"> | |
| Join thousands of creators who trust Omni Creator for unrestricted AI image generation! | |
| </p> | |
| </div> | |
| <!-- Hero Description --> | |
| <div style="text-align: center; margin: 25px auto; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 35px; border-radius: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.1);"> | |
| <h2 style="color: #2c3e50; margin: 0 0 20px 0; font-size: 1.9em; font-weight: 700;"> | |
| π Professional AI Multi-Image Editor - No Restrictions | |
| </h2> | |
| <p style="color: #555; font-size: 1.1em; line-height: 1.6; margin: 0 0 20px 0; padding: 0 20px;"> | |
| Transform multiple images into stunning compositions with our advanced AI multi-image editing platform. Combine, | |
| merge, and blend 2-3 images to create artistic masterpieces, collages, and seamless compositions with complete | |
| creative freedom and professional quality results. | |
| </p> | |
| </div> | |
| <!-- Features Grid --> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 25px; margin: 40px 0;"> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #e74c3c;"> | |
| <h3 style="color: #e74c3c; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| π― Unlimited Generation | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| Premium users enjoy unlimited image generation without daily limits, rate restrictions, or content barriers. | |
| Create as many images as you need, whenever you need them. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #3498db;"> | |
| <h3 style="color: #3498db; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| π No Content Restrictions | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| Generate and edit any type of content without NSFW filters or content limitations. Complete creative | |
| freedom for artists, designers, and content creators. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #27ae60;"> | |
| <h3 style="color: #27ae60; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| β‘ Lightning Fast Processing | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| Advanced AI infrastructure delivers high-quality results in seconds. No waiting in queues, | |
| no processing delays - just instant, professional-grade image editing. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #9b59b6;"> | |
| <h3 style="color: #9b59b6; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| π¨ Advanced Editing Tools | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| Global transformations, precision local editing, style transfer, object removal, background replacement, | |
| and dozens of other professional editing capabilities. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #f39c12;"> | |
| <h3 style="color: #f39c12; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| π Premium Quality | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| State-of-the-art AI models trained on millions of images deliver exceptional quality and realism. | |
| Professional results suitable for commercial use and high-end projects. | |
| </p> | |
| </div> | |
| <div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 5px 20px rgba(0,0,0,0.08); border-left: 5px solid #34495e;"> | |
| <h3 style="color: #34495e; margin: 0 0 15px 0; font-size: 1.4em; font-weight: 600;"> | |
| π Multi-Modal Support | |
| </h3> | |
| <p style="color: #666; margin: 0; line-height: 1.6; font-size: 1em;"> | |
| Support for all image formats, styles, and use cases. From photorealistic portraits to artistic creations, | |
| product photography to digital art - we handle everything. | |
| </p> | |
| </div> | |
| </div> | |
| <!-- Premium Benefits Section --> | |
| <div style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); color: white; padding: 40px; border-radius: 20px; margin: 40px 0; text-align: center;"> | |
| <h2 style="margin: 0 0 25px 0; font-size: 1.8em; font-weight: 700;"> | |
| π Why Choose Omni Creator Premium? | |
| </h2> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0;"> | |
| <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 12px;"> | |
| <h4 style="margin: 0 0 10px 0; font-size: 1.2em;">π« No Rate Limits</h4> | |
| <p style="margin: 0; opacity: 0.9; font-size: 0.95em;">Generate unlimited images without waiting periods or daily restrictions</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 12px;"> | |
| <h4 style="margin: 0 0 10px 0; font-size: 1.2em;">π Unrestricted Content</h4> | |
| <p style="margin: 0; opacity: 0.9; font-size: 0.95em;">Create any type of content without NSFW filters or censorship</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 12px;"> | |
| <h4 style="margin: 0 0 10px 0; font-size: 1.2em;">β‘ Priority Processing</h4> | |
| <p style="margin: 0; opacity: 0.9; font-size: 0.95em;">Skip queues and get instant results with dedicated processing power</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 12px;"> | |
| <h4 style="margin: 0 0 10px 0; font-size: 1.2em;">π¨ Advanced Features</h4> | |
| <p style="margin: 0; opacity: 0.9; font-size: 0.95em;">Access to latest AI models and cutting-edge editing capabilities</p> | |
| </div> | |
| </div> | |
| <div style="display: flex; justify-content: center; margin: 25px 0 0 0;"> | |
| <a href="https://omnicreator.net/multi-image-edit#generator" target="_blank" style=" | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 18px 35px; | |
| background: rgba(255,255,255,0.9); | |
| color: #333; | |
| text-decoration: none; | |
| border-radius: 15px; | |
| font-weight: 700; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 200px; | |
| box-shadow: 0 6px 20px rgba(0,0,0,0.3); | |
| transition: all 0.3s ease; | |
| border: none; | |
| ">π Start Creating Now</a> | |
| </div> | |
| </div> | |
| <!-- Tips Section --> | |
| <div style="background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%); padding: 30px; border-radius: 15px; margin: 40px 0;"> | |
| <h3 style="color: #8b5cf6; text-align: center; margin: 0 0 25px 0; font-size: 1.5em; font-weight: 700;"> | |
| π‘ Pro Tips for Best Results | |
| </h3> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px;"> | |
| <div style="background: rgba(255,255,255,0.85); padding: 18px; border-radius: 12px;"> | |
| <strong style="color: #8b5cf6; font-size: 1.1em;">π Clear Descriptions:</strong> | |
| <p style="color: #555; margin: 5px 0 0 0; line-height: 1.5;">Use detailed, specific prompts for better results. Describe colors, styles, lighting, and composition clearly.</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.85); padding: 18px; border-radius: 12px;"> | |
| <strong style="color: #8b5cf6; font-size: 1.1em;">π― Local Editing:</strong> | |
| <p style="color: #555; margin: 5px 0 0 0; line-height: 1.5;">Use precise brush strokes to select areas for local editing. Smaller, focused edits often yield better results.</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.85); padding: 18px; border-radius: 12px;"> | |
| <strong style="color: #8b5cf6; font-size: 1.1em;">β‘ Iterative Process:</strong> | |
| <p style="color: #555; margin: 5px 0 0 0; line-height: 1.5;">Use "Use as Input" feature to refine results. Multiple iterations can achieve complex transformations.</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.85); padding: 18px; border-radius: 12px;"> | |
| <strong style="color: #8b5cf6; font-size: 1.1em;">πΌοΈ Image Quality:</strong> | |
| <p style="color: #555; margin: 5px 0 0 0; line-height: 1.5;">Higher resolution input images (up to 10MB) generally produce better editing results and finer details.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Use Cases Section --> | |
| <div style="text-align: center; margin: 25px auto; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); padding: 35px; border-radius: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.1);"> | |
| <h2 style="color: #2c3e50; margin: 0 0 20px 0; font-size: 1.8em; font-weight: 700;"> | |
| π Perfect For Every Creative Need | |
| </h2> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 25px 0; text-align: left;"> | |
| <div style="background: rgba(255,255,255,0.8); padding: 20px; border-radius: 12px;"> | |
| <h4 style="color: #e74c3c; margin: 0 0 10px 0;">π¨ Digital Art</h4> | |
| <ul style="color: #555; margin: 0; padding-left: 18px; line-height: 1.6;"> | |
| <li>Character design</li> | |
| <li>Concept art</li> | |
| <li>Style transfer</li> | |
| <li>Artistic effects</li> | |
| </ul> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.8); padding: 20px; border-radius: 12px;"> | |
| <h4 style="color: #3498db; margin: 0 0 10px 0;">πΈ Photography</h4> | |
| <ul style="color: #555; margin: 0; padding-left: 18px; line-height: 1.6;"> | |
| <li>Background replacement</li> | |
| <li>Object removal</li> | |
| <li>Lighting adjustment</li> | |
| <li>Portrait enhancement</li> | |
| </ul> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.8); padding: 20px; border-radius: 12px;"> | |
| <h4 style="color: #27ae60; margin: 0 0 10px 0;">ποΈ E-commerce</h4> | |
| <ul style="color: #555; margin: 0; padding-left: 18px; line-height: 1.6;"> | |
| <li>Product photography</li> | |
| <li>Lifestyle shots</li> | |
| <li>Color variations</li> | |
| <li>Context placement</li> | |
| </ul> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.8); padding: 20px; border-radius: 12px;"> | |
| <h4 style="color: #9b59b6; margin: 0 0 10px 0;">π± Social Media</h4> | |
| <ul style="color: #555; margin: 0; padding-left: 18px; line-height: 1.6;"> | |
| <li>Content creation</li> | |
| <li>Meme generation</li> | |
| <li>Brand visuals</li> | |
| <li>Viral content</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div style="text-align: center; margin: 25px 0 0 0;"> | |
| <a href="https://omnicreator.net/multi-image-edit#generator" target="_blank" style=" | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 18px 35px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 15px; | |
| font-weight: 700; | |
| font-size: 16px; | |
| text-align: center; | |
| min-width: 220px; | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); | |
| transition: all 0.3s ease; | |
| border: none; | |
| ">π― Start Your Project Now</a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Powered by footer --> | |
| <div style="text-align: center; margin: 30px auto 20px auto; padding: 20px;"> | |
| <p style="margin: 0 0 10px 0; font-size: 18px; color: #333; font-weight: 500;"> | |
| Powered by <a href="https://omnicreator.net/multi-image-edit#generator" target="_blank" style="color: #667eea; text-decoration: none; font-weight: bold;">Omni Creator</a> | |
| </p> | |
| <p style="margin: 0; font-size: 14px; color: #999; font-weight: 400;"> | |
| The ultimate AI image generation and editing platform β’ Unlimited creativity, zero restrictions | |
| </p> | |
| </div> | |
| """, padding=False) | |
| return app | |
| if __name__ == "__main__": | |
| app = create_app() | |
| # Improve queue configuration to handle high concurrency and prevent SSE connection issues | |
| app.queue( | |
| default_concurrency_limit=20, # Default concurrency limit | |
| max_size=50, # Maximum queue size | |
| api_open=False # Close API access to reduce resource consumption | |
| ) | |
| app.launch( | |
| server_name="0.0.0.0", | |
| show_error=True, # Show detailed error information | |
| quiet=False, # Keep log output | |
| max_threads=40, # Increase thread pool size | |
| height=800, | |
| favicon_path=None # Reduce resource loading | |
| ) | |