Spaces:
Running
Running
Commit
·
b15d785
1
Parent(s):
243318e
init
Browse files
app.py
CHANGED
|
@@ -5,7 +5,7 @@ import shutil
|
|
| 5 |
import tempfile
|
| 6 |
import time
|
| 7 |
import json
|
| 8 |
-
from util import process_image_edit,
|
| 9 |
from nfsw import NSFWDetector
|
| 10 |
|
| 11 |
# i18n
|
|
@@ -58,7 +58,7 @@ IP_Query_Results = {} # Track async query results
|
|
| 58 |
|
| 59 |
# Restricted countries list (these countries have lower usage limits)
|
| 60 |
RESTRICTED_COUNTRIES = ["印度", "巴基斯坦"]
|
| 61 |
-
RESTRICTED_COUNTRY_LIMIT =
|
| 62 |
|
| 63 |
country_dict = {
|
| 64 |
"zh": ["中国", "香港"],
|
|
@@ -724,11 +724,8 @@ def edit_image_interface(input_image, prompt, lang, request: gr.Request, progres
|
|
| 724 |
# Generate action buttons HTML like Trump AI Voice
|
| 725 |
action_buttons_html = ""
|
| 726 |
if task_uuid:
|
| 727 |
-
# Create
|
| 728 |
-
|
| 729 |
-
# Use result URL as end_image, original upload URL as input_image
|
| 730 |
-
encoded_prompt = quote(prompt.strip())
|
| 731 |
-
image_to_video_url = f"https://omnicreator.net/image-to-video?input_image={input_image_url}&end_image={result_url}&prompt={encoded_prompt}"
|
| 732 |
action_buttons_html = f"""
|
| 733 |
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 734 |
<a href='https://omnicreator.net/#generator' target='_blank' style='
|
|
@@ -748,7 +745,7 @@ def edit_image_interface(input_image, prompt, lang, request: gr.Request, progres
|
|
| 748 |
transition: all 0.3s ease;
|
| 749 |
border: none;
|
| 750 |
'>🚀 Unlimited Generation</a>
|
| 751 |
-
<a href='{
|
| 752 |
display: inline-flex;
|
| 753 |
align-items: center;
|
| 754 |
justify-content: center;
|
|
@@ -764,7 +761,7 @@ def edit_image_interface(input_image, prompt, lang, request: gr.Request, progres
|
|
| 764 |
box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
|
| 765 |
transition: all 0.3s ease;
|
| 766 |
border: none;
|
| 767 |
-
'>&#
|
| 768 |
</div>
|
| 769 |
"""
|
| 770 |
|
|
@@ -799,370 +796,6 @@ def edit_image_interface(input_image, prompt, lang, request: gr.Request, progres
|
|
| 799 |
print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
|
| 800 |
return None, t("error_processing_exception", lang).format(error=str(e)), gr.update(visible=False)
|
| 801 |
|
| 802 |
-
def local_edit_interface(image_dict, prompt, reference_image, lang, request: gr.Request, progress=gr.Progress()):
|
| 803 |
-
"""
|
| 804 |
-
Handle local editing requests (with phase-based limitations)
|
| 805 |
-
"""
|
| 806 |
-
try:
|
| 807 |
-
# Extract user IP
|
| 808 |
-
client_ip = request.client.host
|
| 809 |
-
x_forwarded_for = dict(request.headers).get('x-forwarded-for')
|
| 810 |
-
if x_forwarded_for:
|
| 811 |
-
client_ip = x_forwarded_for
|
| 812 |
-
if client_ip not in IP_Dict:
|
| 813 |
-
IP_Dict[client_ip] = 0
|
| 814 |
-
IP_Dict[client_ip] += 1
|
| 815 |
-
|
| 816 |
-
if image_dict is None:
|
| 817 |
-
return None, t("error_upload_and_draw", lang), gr.update(visible=False)
|
| 818 |
-
|
| 819 |
-
# Handle different input formats for ImageEditor
|
| 820 |
-
if isinstance(image_dict, dict):
|
| 821 |
-
# ImageEditor dict format
|
| 822 |
-
if "background" not in image_dict or "layers" not in image_dict:
|
| 823 |
-
return None, t("error_draw_on_image", lang), gr.update(visible=False)
|
| 824 |
-
|
| 825 |
-
base_image = image_dict["background"]
|
| 826 |
-
layers = image_dict["layers"]
|
| 827 |
-
|
| 828 |
-
# Special handling: if background is None but composite exists, use composite
|
| 829 |
-
if base_image is None and "composite" in image_dict and image_dict["composite"] is not None:
|
| 830 |
-
print("🔧 Background is None, using composite instead")
|
| 831 |
-
base_image = image_dict["composite"]
|
| 832 |
-
else:
|
| 833 |
-
# Simple case: Direct PIL Image (from example)
|
| 834 |
-
base_image = image_dict
|
| 835 |
-
layers = []
|
| 836 |
-
|
| 837 |
-
# Check for special example case - bypass mask requirement
|
| 838 |
-
is_example_case = prompt and prompt.startswith("EXAMPLE_PANDA_CAT_")
|
| 839 |
-
|
| 840 |
-
# Debug: check current state
|
| 841 |
-
if is_example_case:
|
| 842 |
-
print(f"🔍 Example case detected - base_image is None: {base_image is None}")
|
| 843 |
-
|
| 844 |
-
# Special handling for example case: load image directly from file
|
| 845 |
-
if is_example_case and base_image is None:
|
| 846 |
-
try:
|
| 847 |
-
from PIL import Image
|
| 848 |
-
import os
|
| 849 |
-
|
| 850 |
-
main_path = "datas/panda01.jpeg"
|
| 851 |
-
print(f"🔍 Trying to load: {main_path}, exists: {os.path.exists(main_path)}")
|
| 852 |
-
|
| 853 |
-
if os.path.exists(main_path):
|
| 854 |
-
base_image = Image.open(main_path)
|
| 855 |
-
print(f"✅ Successfully loaded example image: {base_image.size}")
|
| 856 |
-
else:
|
| 857 |
-
return None, f"❌ Example image not found: {main_path}", gr.update(visible=False)
|
| 858 |
-
except Exception as e:
|
| 859 |
-
return None, f"❌ Failed to load example image: {str(e)}", gr.update(visible=False)
|
| 860 |
-
|
| 861 |
-
# Additional check for base_image
|
| 862 |
-
if base_image is None:
|
| 863 |
-
if is_example_case:
|
| 864 |
-
print(f"❌ Example case but base_image still None!")
|
| 865 |
-
return None, t("error_no_image_found", lang), gr.update(visible=False)
|
| 866 |
-
|
| 867 |
-
if not layers and not is_example_case:
|
| 868 |
-
return None, t("error_draw_on_image", lang), gr.update(visible=False)
|
| 869 |
-
|
| 870 |
-
if not prompt or prompt.strip() == "":
|
| 871 |
-
return None, t("error_enter_prompt", lang), gr.update(visible=False)
|
| 872 |
-
|
| 873 |
-
# Check prompt length
|
| 874 |
-
if len(prompt.strip()) <= 3:
|
| 875 |
-
return None, t("error_prompt_too_short", lang), gr.update(visible=False)
|
| 876 |
-
except Exception as e:
|
| 877 |
-
print(f"⚠️ Local edit request preprocessing error: {e}")
|
| 878 |
-
return None, t("error_request_processing", lang), gr.update(visible=False)
|
| 879 |
-
|
| 880 |
-
# Get user current phase
|
| 881 |
-
current_phase = get_ip_phase(client_ip)
|
| 882 |
-
current_count = get_ip_generation_count(client_ip)
|
| 883 |
-
geo_info = IP_Country_Cache.get(client_ip, {"country": "Unknown", "region": "Unknown", "city": "Unknown"})
|
| 884 |
-
|
| 885 |
-
print(f"📊 Local edit user phase info - IP: {client_ip}, Location: {geo_info['country']}/{geo_info['region']}/{geo_info['city']}, Phase: {current_phase}, Count: {current_count}")
|
| 886 |
-
|
| 887 |
-
# Check if user reached the like button tip threshold
|
| 888 |
-
show_like_tip = (current_count >= TIP_TRY_N)
|
| 889 |
-
|
| 890 |
-
# Check if completely blocked
|
| 891 |
-
if current_phase == 'blocked':
|
| 892 |
-
# Generate blocked limit button
|
| 893 |
-
blocked_button_html = f"""
|
| 894 |
-
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 895 |
-
<a href='https://omnicreator.net/#generator' target='_blank' style='
|
| 896 |
-
display: inline-flex;
|
| 897 |
-
align-items: center;
|
| 898 |
-
justify-content: center;
|
| 899 |
-
padding: 16px 32px;
|
| 900 |
-
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
| 901 |
-
color: white;
|
| 902 |
-
text-decoration: none;
|
| 903 |
-
border-radius: 12px;
|
| 904 |
-
font-weight: 600;
|
| 905 |
-
font-size: 16px;
|
| 906 |
-
text-align: center;
|
| 907 |
-
min-width: 200px;
|
| 908 |
-
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
|
| 909 |
-
transition: all 0.3s ease;
|
| 910 |
-
border: none;
|
| 911 |
-
'>🚀 Unlimited Generation</a>
|
| 912 |
-
</div>
|
| 913 |
-
"""
|
| 914 |
-
|
| 915 |
-
# Use same message for all users to avoid discrimination perception
|
| 916 |
-
blocked_message = t("error_free_limit_reached", lang)
|
| 917 |
-
|
| 918 |
-
return None, blocked_message, gr.update(value=blocked_button_html, visible=True)
|
| 919 |
-
|
| 920 |
-
# Check rate limit (applies to rate_limit phases)
|
| 921 |
-
if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3']:
|
| 922 |
-
is_limited, wait_minutes, window_count = check_rate_limit_for_phase(client_ip, current_phase)
|
| 923 |
-
if is_limited:
|
| 924 |
-
wait_minutes_int = int(wait_minutes) + 1
|
| 925 |
-
# Generate rate limit button
|
| 926 |
-
rate_limit_button_html = f"""
|
| 927 |
-
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 928 |
-
<a href='https://omnicreator.net/#generator' target='_blank' style='
|
| 929 |
-
display: inline-flex;
|
| 930 |
-
align-items: center;
|
| 931 |
-
justify-content: center;
|
| 932 |
-
padding: 16px 32px;
|
| 933 |
-
background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%);
|
| 934 |
-
color: white;
|
| 935 |
-
text-decoration: none;
|
| 936 |
-
border-radius: 12px;
|
| 937 |
-
font-weight: 600;
|
| 938 |
-
font-size: 16px;
|
| 939 |
-
text-align: center;
|
| 940 |
-
min-width: 200px;
|
| 941 |
-
box-shadow: 0 4px 15px rgba(243, 156, 18, 0.4);
|
| 942 |
-
transition: all 0.3s ease;
|
| 943 |
-
border: none;
|
| 944 |
-
'>⏰ Skip Wait - Unlimited Generation</a>
|
| 945 |
-
</div>
|
| 946 |
-
"""
|
| 947 |
-
return None, t("error_free_limit_wait", lang).format(wait_minutes_int=wait_minutes_int), gr.update(value=rate_limit_button_html, visible=True)
|
| 948 |
-
|
| 949 |
-
# Handle NSFW detection based on phase
|
| 950 |
-
is_nsfw_task = False # Track if this task involves NSFW content
|
| 951 |
-
|
| 952 |
-
# Skip NSFW detection in free phase
|
| 953 |
-
if current_phase != 'free' and nsfw_detector is not None and base_image is not None:
|
| 954 |
-
try:
|
| 955 |
-
nsfw_result = nsfw_detector.predict_pil_label_only(base_image)
|
| 956 |
-
|
| 957 |
-
if nsfw_result.lower() == "nsfw":
|
| 958 |
-
is_nsfw_task = True
|
| 959 |
-
print(f"🔍 Local edit input NSFW detected in {current_phase} phase: ❌❌❌ {nsfw_result} - IP: {client_ip} (will blur result)")
|
| 960 |
-
else:
|
| 961 |
-
print(f"🔍 Local edit input NSFW check passed: ✅✅✅ {nsfw_result} - IP: {client_ip}")
|
| 962 |
-
|
| 963 |
-
except Exception as e:
|
| 964 |
-
print(f"⚠️ Local edit input NSFW detection failed: {e}")
|
| 965 |
-
# Allow continuation when detection fails
|
| 966 |
-
|
| 967 |
-
result_url = None
|
| 968 |
-
status_message = ""
|
| 969 |
-
|
| 970 |
-
def progress_callback(message):
|
| 971 |
-
try:
|
| 972 |
-
nonlocal status_message
|
| 973 |
-
status_message = message
|
| 974 |
-
# Add error handling to prevent progress update failure
|
| 975 |
-
if progress is not None:
|
| 976 |
-
# Enhanced progress display with better formatting for local editing
|
| 977 |
-
if "Queue:" in message or "tasks ahead" in message:
|
| 978 |
-
# Queue status - show with different progress value to indicate waiting
|
| 979 |
-
progress(0.1, desc=message)
|
| 980 |
-
elif "Processing" in message or "AI is processing" in message:
|
| 981 |
-
# Processing status
|
| 982 |
-
progress(0.7, desc=message)
|
| 983 |
-
elif "Generating" in message or "Almost done" in message:
|
| 984 |
-
# Generation status
|
| 985 |
-
progress(0.9, desc=message)
|
| 986 |
-
else:
|
| 987 |
-
# Default status
|
| 988 |
-
progress(0.5, desc=message)
|
| 989 |
-
except Exception as e:
|
| 990 |
-
print(f"⚠️ Local edit progress update failed: {e}")
|
| 991 |
-
|
| 992 |
-
try:
|
| 993 |
-
# Record generation attempt (before actual generation to ensure correct count)
|
| 994 |
-
record_generation_attempt(client_ip, current_phase)
|
| 995 |
-
updated_count = get_ip_generation_count(client_ip)
|
| 996 |
-
|
| 997 |
-
print(f"✅ Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
|
| 998 |
-
|
| 999 |
-
# Clean prompt for API call
|
| 1000 |
-
clean_prompt = prompt.strip()
|
| 1001 |
-
if clean_prompt.startswith("EXAMPLE_PANDA_CAT_"):
|
| 1002 |
-
clean_prompt = clean_prompt[18:] # Remove the prefix
|
| 1003 |
-
|
| 1004 |
-
# Call local image editing processing function
|
| 1005 |
-
if is_example_case:
|
| 1006 |
-
# For example case, pass special flag to use local mask file
|
| 1007 |
-
input_image_url, result_url, message, task_uuid = process_local_image_edit(base_image, layers, clean_prompt, reference_image, progress_callback, use_example_mask="datas/panda01m.jpeg")
|
| 1008 |
-
else:
|
| 1009 |
-
# Normal case
|
| 1010 |
-
input_image_url, result_url, message, task_uuid = process_local_image_edit(base_image, layers, clean_prompt, reference_image, progress_callback)
|
| 1011 |
-
|
| 1012 |
-
if result_url:
|
| 1013 |
-
print(f"✅ Local editing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
|
| 1014 |
-
|
| 1015 |
-
# Detect result image NSFW content (only in rate limit phases)
|
| 1016 |
-
if nsfw_detector is not None and current_phase != 'free':
|
| 1017 |
-
try:
|
| 1018 |
-
if progress is not None:
|
| 1019 |
-
progress(0.9, desc=t("status_checking_result", lang))
|
| 1020 |
-
|
| 1021 |
-
is_nsfw, nsfw_error = download_and_check_result_nsfw(result_url, nsfw_detector)
|
| 1022 |
-
|
| 1023 |
-
if nsfw_error:
|
| 1024 |
-
print(f"⚠️ Local edit result image NSFW detection error - IP: {client_ip}, error: {nsfw_error}")
|
| 1025 |
-
elif is_nsfw:
|
| 1026 |
-
is_nsfw_task = True # Mark task as NSFW
|
| 1027 |
-
print(f"🔍 Local edit result image NSFW detected in {current_phase} phase: ❌❌❌ - IP: {client_ip} (will blur result)")
|
| 1028 |
-
else:
|
| 1029 |
-
print(f"🔍 Local edit result image NSFW check passed: ✅✅✅ - IP: {client_ip}")
|
| 1030 |
-
|
| 1031 |
-
except Exception as e:
|
| 1032 |
-
print(f"⚠️ Local edit result image NSFW detection exception - IP: {client_ip}, error: {str(e)}")
|
| 1033 |
-
|
| 1034 |
-
# Apply blur if this is an NSFW task in rate limit phases
|
| 1035 |
-
should_blur = False
|
| 1036 |
-
|
| 1037 |
-
if current_phase in ['rate_limit_1', 'rate_limit_2', 'rate_limit_3'] and is_nsfw_task:
|
| 1038 |
-
should_blur = True
|
| 1039 |
-
|
| 1040 |
-
# Apply blur processing
|
| 1041 |
-
if should_blur:
|
| 1042 |
-
if progress is not None:
|
| 1043 |
-
progress(0.95, desc=t("status_applying_filter", lang))
|
| 1044 |
-
|
| 1045 |
-
blurred_image = apply_gaussian_blur_to_image_url(result_url)
|
| 1046 |
-
if blurred_image is not None:
|
| 1047 |
-
final_result = blurred_image # Return PIL Image object
|
| 1048 |
-
final_message = t("warning_content_filter", lang)
|
| 1049 |
-
print(f"🔒 Local edit applied Gaussian blur for NSFW content - IP: {client_ip}")
|
| 1050 |
-
else:
|
| 1051 |
-
# Blur failed, return original URL with warning
|
| 1052 |
-
final_result = result_url
|
| 1053 |
-
final_message = t("warning_content_review", lang)
|
| 1054 |
-
|
| 1055 |
-
# Generate NSFW button for blurred content
|
| 1056 |
-
nsfw_action_buttons_html = f"""
|
| 1057 |
-
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 1058 |
-
<a href='https://omnicreator.net/#generator' target='_blank' style='
|
| 1059 |
-
display: inline-flex;
|
| 1060 |
-
align-items: center;
|
| 1061 |
-
justify-content: center;
|
| 1062 |
-
padding: 16px 32px;
|
| 1063 |
-
background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
|
| 1064 |
-
color: white;
|
| 1065 |
-
text-decoration: none;
|
| 1066 |
-
border-radius: 12px;
|
| 1067 |
-
font-weight: 600;
|
| 1068 |
-
font-size: 16px;
|
| 1069 |
-
text-align: center;
|
| 1070 |
-
min-width: 200px;
|
| 1071 |
-
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
|
| 1072 |
-
transition: all 0.3s ease;
|
| 1073 |
-
border: none;
|
| 1074 |
-
'>🔥 Unlimited Creative Generation</a>
|
| 1075 |
-
</div>
|
| 1076 |
-
"""
|
| 1077 |
-
return final_result, final_message, gr.update(value=nsfw_action_buttons_html, visible=True)
|
| 1078 |
-
else:
|
| 1079 |
-
final_result = result_url
|
| 1080 |
-
final_message = t("status_completed_message", lang).format(message=message)
|
| 1081 |
-
|
| 1082 |
-
try:
|
| 1083 |
-
if progress is not None:
|
| 1084 |
-
progress(1.0, desc=t("status_processing_completed", lang))
|
| 1085 |
-
except Exception as e:
|
| 1086 |
-
print(f"⚠️ Local edit final progress update failed: {e}")
|
| 1087 |
-
|
| 1088 |
-
# Generate action buttons HTML like Trump AI Voice
|
| 1089 |
-
action_buttons_html = ""
|
| 1090 |
-
if task_uuid:
|
| 1091 |
-
# Create image-to-video URL with input image, end image, and prompt
|
| 1092 |
-
from urllib.parse import quote
|
| 1093 |
-
# Use result URL as end_image, original upload URL as input_image
|
| 1094 |
-
encoded_prompt = quote(clean_prompt)
|
| 1095 |
-
image_to_video_url = f"https://omnicreator.net/image-to-video?input_image={input_image_url}&end_image={result_url}&prompt={encoded_prompt}"
|
| 1096 |
-
action_buttons_html = f"""
|
| 1097 |
-
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 1098 |
-
<a href='https://omnicreator.net/local-inpaint' target='_blank' style='
|
| 1099 |
-
display: inline-flex;
|
| 1100 |
-
align-items: center;
|
| 1101 |
-
justify-content: center;
|
| 1102 |
-
padding: 16px 32px;
|
| 1103 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 1104 |
-
color: white;
|
| 1105 |
-
text-decoration: none;
|
| 1106 |
-
border-radius: 12px;
|
| 1107 |
-
font-weight: 600;
|
| 1108 |
-
font-size: 16px;
|
| 1109 |
-
text-align: center;
|
| 1110 |
-
min-width: 160px;
|
| 1111 |
-
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
| 1112 |
-
transition: all 0.3s ease;
|
| 1113 |
-
border: none;
|
| 1114 |
-
'>🚀 Unlimited Generation</a>
|
| 1115 |
-
<a href='{image_to_video_url}' target='_blank' style='
|
| 1116 |
-
display: inline-flex;
|
| 1117 |
-
align-items: center;
|
| 1118 |
-
justify-content: center;
|
| 1119 |
-
padding: 16px 32px;
|
| 1120 |
-
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
| 1121 |
-
color: white;
|
| 1122 |
-
text-decoration: none;
|
| 1123 |
-
border-radius: 12px;
|
| 1124 |
-
font-weight: 600;
|
| 1125 |
-
font-size: 16px;
|
| 1126 |
-
text-align: center;
|
| 1127 |
-
min-width: 160px;
|
| 1128 |
-
box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
|
| 1129 |
-
transition: all 0.3s ease;
|
| 1130 |
-
border: none;
|
| 1131 |
-
'>🎥 Turn Image to Video</a>
|
| 1132 |
-
</div>
|
| 1133 |
-
"""
|
| 1134 |
-
|
| 1135 |
-
# Add popup script if needed (using different approach)
|
| 1136 |
-
if show_like_tip:
|
| 1137 |
-
action_buttons_html += """
|
| 1138 |
-
<div style='display: flex; justify-content: center; margin: 15px 0 5px 0; padding: 0px;'>
|
| 1139 |
-
<div style='
|
| 1140 |
-
display: inline-flex;
|
| 1141 |
-
align-items: center;
|
| 1142 |
-
justify-content: center;
|
| 1143 |
-
padding: 12px 24px;
|
| 1144 |
-
background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
|
| 1145 |
-
color: white;
|
| 1146 |
-
border-radius: 10px;
|
| 1147 |
-
font-weight: 600;
|
| 1148 |
-
font-size: 14px;
|
| 1149 |
-
text-align: center;
|
| 1150 |
-
max-width: 400px;
|
| 1151 |
-
box-shadow: 0 3px 12px rgba(255, 107, 107, 0.3);
|
| 1152 |
-
border: none;
|
| 1153 |
-
'>👉 Click the ❤️ Like button to unlock more free trial attempts!</div>
|
| 1154 |
-
</div>
|
| 1155 |
-
"""
|
| 1156 |
-
|
| 1157 |
-
return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
|
| 1158 |
-
else:
|
| 1159 |
-
print(f"❌ Local editing processing failed - IP: {client_ip}, error: {message}", flush=True)
|
| 1160 |
-
return None, t("error_processing_failed", lang).format(message=message), gr.update(visible=False)
|
| 1161 |
-
|
| 1162 |
-
except Exception as e:
|
| 1163 |
-
print(f"❌ Local editing exception - IP: {client_ip}, error: {str(e)}")
|
| 1164 |
-
return None, t("error_processing_exception", lang).format(error=str(e)), gr.update(visible=False)
|
| 1165 |
-
|
| 1166 |
# Create Gradio interface
|
| 1167 |
def create_app():
|
| 1168 |
with gr.Blocks(
|
|
@@ -1429,162 +1062,6 @@ def create_app():
|
|
| 1429 |
inputs=[output_image],
|
| 1430 |
outputs=[input_image]
|
| 1431 |
)
|
| 1432 |
-
|
| 1433 |
-
with gr.Tab(t("local_inpaint_tab", "en")) as local_tab:
|
| 1434 |
-
with gr.Row():
|
| 1435 |
-
with gr.Column(scale=1):
|
| 1436 |
-
upload_and_draw_mask_header = gr.Markdown(t("upload_and_draw_mask_header", "en"))
|
| 1437 |
-
local_input_image = gr.ImageEditor(
|
| 1438 |
-
label=t("upload_and_draw_mask_label", "en"),
|
| 1439 |
-
type="pil",
|
| 1440 |
-
height=512,
|
| 1441 |
-
brush=gr.Brush(colors=["#ff0000"], default_size=180),
|
| 1442 |
-
elem_classes=["upload-area"]
|
| 1443 |
-
)
|
| 1444 |
-
|
| 1445 |
-
reference_image_header = gr.Markdown(t("reference_image_header", "en"))
|
| 1446 |
-
local_reference_image = gr.Image(
|
| 1447 |
-
label=t("reference_image_label", "en"),
|
| 1448 |
-
type="pil",
|
| 1449 |
-
height=256
|
| 1450 |
-
)
|
| 1451 |
-
|
| 1452 |
-
local_editing_instructions_header = gr.Markdown(t("editing_instructions_header", "en"))
|
| 1453 |
-
local_prompt_input = gr.Textbox(
|
| 1454 |
-
label=t("local_prompt_input_label", "en"),
|
| 1455 |
-
placeholder=t("local_prompt_input_placeholder", "en"),
|
| 1456 |
-
lines=3,
|
| 1457 |
-
max_lines=5
|
| 1458 |
-
)
|
| 1459 |
-
|
| 1460 |
-
local_edit_button = gr.Button(
|
| 1461 |
-
t("start_local_editing_button", "en"),
|
| 1462 |
-
variant="primary",
|
| 1463 |
-
size="lg"
|
| 1464 |
-
)
|
| 1465 |
-
|
| 1466 |
-
with gr.Column(scale=1):
|
| 1467 |
-
local_editing_result_header = gr.Markdown(t("editing_result_header", "en"))
|
| 1468 |
-
local_output_image = gr.Image(
|
| 1469 |
-
label=t("local_output_image_label", "en"),
|
| 1470 |
-
height=320,
|
| 1471 |
-
elem_classes=["result-area"]
|
| 1472 |
-
)
|
| 1473 |
-
|
| 1474 |
-
local_use_as_input_btn = gr.Button(
|
| 1475 |
-
t("use_as_input_button", "en"),
|
| 1476 |
-
variant="secondary",
|
| 1477 |
-
size="sm",
|
| 1478 |
-
elem_classes=["use-as-input-btn"]
|
| 1479 |
-
)
|
| 1480 |
-
|
| 1481 |
-
local_status_output = gr.Textbox(
|
| 1482 |
-
label=t("status_output_label", "en"),
|
| 1483 |
-
lines=2,
|
| 1484 |
-
max_lines=3,
|
| 1485 |
-
interactive=False
|
| 1486 |
-
)
|
| 1487 |
-
|
| 1488 |
-
local_action_buttons = gr.HTML(visible=False)
|
| 1489 |
-
|
| 1490 |
-
local_edit_button.click(
|
| 1491 |
-
fn=local_edit_interface,
|
| 1492 |
-
inputs=[local_input_image, local_prompt_input, local_reference_image, lang_state],
|
| 1493 |
-
outputs=[local_output_image, local_status_output, local_action_buttons],
|
| 1494 |
-
show_progress=True,
|
| 1495 |
-
concurrency_limit=20
|
| 1496 |
-
)
|
| 1497 |
-
|
| 1498 |
-
def simple_local_use_as_input(output_img):
|
| 1499 |
-
if output_img is not None:
|
| 1500 |
-
return {
|
| 1501 |
-
"background": output_img,
|
| 1502 |
-
"layers": [],
|
| 1503 |
-
"composite": output_img
|
| 1504 |
-
}
|
| 1505 |
-
return None
|
| 1506 |
-
|
| 1507 |
-
local_use_as_input_btn.click(
|
| 1508 |
-
fn=simple_local_use_as_input,
|
| 1509 |
-
inputs=[local_output_image],
|
| 1510 |
-
outputs=[local_input_image]
|
| 1511 |
-
)
|
| 1512 |
-
|
| 1513 |
-
# Local inpaint example
|
| 1514 |
-
local_inpaint_example_header = gr.Markdown(t("local_inpaint_example_header", "en"))
|
| 1515 |
-
|
| 1516 |
-
def load_local_example():
|
| 1517 |
-
"""Load panda to cat transformation example - simplified, mask handled in backend"""
|
| 1518 |
-
try:
|
| 1519 |
-
from PIL import Image
|
| 1520 |
-
import os
|
| 1521 |
-
|
| 1522 |
-
# Check file paths
|
| 1523 |
-
main_path = "datas/panda01.jpeg"
|
| 1524 |
-
ref_path = "datas/cat01.webp"
|
| 1525 |
-
|
| 1526 |
-
# Load main image
|
| 1527 |
-
if not os.path.exists(main_path):
|
| 1528 |
-
return None, None, "EXAMPLE_PANDA_CAT_let the cat ride on the panda"
|
| 1529 |
-
|
| 1530 |
-
main_img = Image.open(main_path)
|
| 1531 |
-
|
| 1532 |
-
# Load reference image
|
| 1533 |
-
if not os.path.exists(ref_path):
|
| 1534 |
-
ref_img = None
|
| 1535 |
-
else:
|
| 1536 |
-
ref_img = Image.open(ref_path)
|
| 1537 |
-
|
| 1538 |
-
# ImageEditor format
|
| 1539 |
-
editor_data = {
|
| 1540 |
-
"background": main_img,
|
| 1541 |
-
"layers": [],
|
| 1542 |
-
"composite": main_img
|
| 1543 |
-
}
|
| 1544 |
-
|
| 1545 |
-
# Special prompt to indicate this is the example case
|
| 1546 |
-
prompt = "EXAMPLE_PANDA_CAT_let the cat ride on the panda"
|
| 1547 |
-
|
| 1548 |
-
# Return just the PIL image instead of dict format to avoid UI state issues
|
| 1549 |
-
return main_img, ref_img, prompt
|
| 1550 |
-
|
| 1551 |
-
except Exception as e:
|
| 1552 |
-
return None, None, "EXAMPLE_PANDA_CAT_Transform the panda head into a cute cat head, keeping the body"
|
| 1553 |
-
|
| 1554 |
-
# Example display
|
| 1555 |
-
panda_to_cat_example_header = gr.Markdown(t("panda_to_cat_example_header", "en"))
|
| 1556 |
-
with gr.Row():
|
| 1557 |
-
with gr.Column(scale=2):
|
| 1558 |
-
# Preview images for local example
|
| 1559 |
-
with gr.Row():
|
| 1560 |
-
try:
|
| 1561 |
-
gr.Image("datas/panda01.jpeg", label=t("main_image_label", "en"), height=120, width=120, show_label=True, interactive=False)
|
| 1562 |
-
gr.Image("datas/panda01m.jpeg", label=t("mask_label", "en"), height=120, width=120, show_label=True, interactive=False)
|
| 1563 |
-
gr.Image("datas/cat01.webp", label=t("reference_label", "en"), height=120, width=120, show_label=True, interactive=False)
|
| 1564 |
-
except:
|
| 1565 |
-
gr.Markdown("*Preview images not available*")
|
| 1566 |
-
panda_example_note = gr.Markdown(t("panda_example_note", "en"))
|
| 1567 |
-
with gr.Column(scale=1):
|
| 1568 |
-
load_panda_example_button = gr.Button(
|
| 1569 |
-
t("load_panda_example_button", "en"),
|
| 1570 |
-
size="lg",
|
| 1571 |
-
variant="secondary"
|
| 1572 |
-
)
|
| 1573 |
-
load_panda_example_button.click(
|
| 1574 |
-
fn=load_local_example,
|
| 1575 |
-
outputs=[local_input_image, local_reference_image, local_prompt_input]
|
| 1576 |
-
)
|
| 1577 |
-
|
| 1578 |
-
# Add a refresh button to fix UI state issues
|
| 1579 |
-
refresh_editor_button = gr.Button(
|
| 1580 |
-
t("refresh_editor_button", "en"),
|
| 1581 |
-
size="sm",
|
| 1582 |
-
variant="secondary"
|
| 1583 |
-
)
|
| 1584 |
-
refresh_editor_button.click(
|
| 1585 |
-
fn=lambda: gr.update(),
|
| 1586 |
-
outputs=[local_input_image]
|
| 1587 |
-
)
|
| 1588 |
|
| 1589 |
# SEO Content Section
|
| 1590 |
seo_html = gr.HTML()
|
|
@@ -1782,10 +1259,6 @@ def create_app():
|
|
| 1782 |
header_title, news_banner,
|
| 1783 |
global_tab, upload_image_header, input_image, editing_instructions_header, prompt_input, edit_button,
|
| 1784 |
editing_result_header, output_image, use_as_input_btn, status_output, prompt_examples_header,
|
| 1785 |
-
local_tab, upload_and_draw_mask_header, local_input_image, reference_image_header, local_reference_image,
|
| 1786 |
-
local_editing_instructions_header, local_prompt_input, local_edit_button, local_editing_result_header,
|
| 1787 |
-
local_output_image, local_use_as_input_btn, local_status_output, local_inpaint_example_header,
|
| 1788 |
-
panda_to_cat_example_header, panda_example_note, load_panda_example_button, refresh_editor_button,
|
| 1789 |
seo_html,
|
| 1790 |
]
|
| 1791 |
|
|
@@ -1821,23 +1294,6 @@ def create_app():
|
|
| 1821 |
use_as_input_btn: gr.update(value=t("use_as_input_button", lang)),
|
| 1822 |
status_output: gr.update(label=t("status_output_label", lang)),
|
| 1823 |
prompt_examples_header: gr.update(value=t("prompt_examples_header", lang)),
|
| 1824 |
-
local_tab: gr.update(label=t("local_inpaint_tab", lang)),
|
| 1825 |
-
upload_and_draw_mask_header: gr.update(value=t("upload_and_draw_mask_header", lang)),
|
| 1826 |
-
local_input_image: gr.update(label=t("upload_and_draw_mask_label", lang)),
|
| 1827 |
-
reference_image_header: gr.update(value=t("reference_image_header", lang)),
|
| 1828 |
-
local_reference_image: gr.update(label=t("reference_image_label", lang)),
|
| 1829 |
-
local_editing_instructions_header: gr.update(value=t("editing_instructions_header", lang)),
|
| 1830 |
-
local_prompt_input: gr.update(label=t("local_prompt_input_label", lang), placeholder=t("local_prompt_input_placeholder", lang)),
|
| 1831 |
-
local_edit_button: gr.update(value=t("start_local_editing_button", lang)),
|
| 1832 |
-
local_editing_result_header: gr.update(value=t("editing_result_header", lang)),
|
| 1833 |
-
local_output_image: gr.update(label=t("local_output_image_label", lang)),
|
| 1834 |
-
local_use_as_input_btn: gr.update(value=t("use_as_input_button", lang)),
|
| 1835 |
-
local_status_output: gr.update(label=t("status_output_label", lang)),
|
| 1836 |
-
local_inpaint_example_header: gr.update(value=t("local_inpaint_example_header", lang)),
|
| 1837 |
-
panda_to_cat_example_header: gr.update(value=t("panda_to_cat_example_header", lang)),
|
| 1838 |
-
panda_example_note: gr.update(value=t("panda_example_note", lang)),
|
| 1839 |
-
load_panda_example_button: gr.update(value=t("load_panda_example_button", lang)),
|
| 1840 |
-
refresh_editor_button: gr.update(value=t("refresh_editor_button", lang)),
|
| 1841 |
seo_html: gr.update(value=get_seo_html(lang)),
|
| 1842 |
}
|
| 1843 |
|
|
|
|
| 5 |
import tempfile
|
| 6 |
import time
|
| 7 |
import json
|
| 8 |
+
from util import process_image_edit, download_and_check_result_nsfw
|
| 9 |
from nfsw import NSFWDetector
|
| 10 |
|
| 11 |
# i18n
|
|
|
|
| 58 |
|
| 59 |
# Restricted countries list (these countries have lower usage limits)
|
| 60 |
RESTRICTED_COUNTRIES = ["印度", "巴基斯坦"]
|
| 61 |
+
RESTRICTED_COUNTRY_LIMIT = 3 # Max usage for restricted countries
|
| 62 |
|
| 63 |
country_dict = {
|
| 64 |
"zh": ["中国", "香港"],
|
|
|
|
| 724 |
# Generate action buttons HTML like Trump AI Voice
|
| 725 |
action_buttons_html = ""
|
| 726 |
if task_uuid:
|
| 727 |
+
# Create task detail URL for downloading HD image
|
| 728 |
+
task_detail_url = f"https://omnicreator.net/my-creations/task/{task_uuid}"
|
|
|
|
|
|
|
|
|
|
| 729 |
action_buttons_html = f"""
|
| 730 |
<div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
|
| 731 |
<a href='https://omnicreator.net/#generator' target='_blank' style='
|
|
|
|
| 745 |
transition: all 0.3s ease;
|
| 746 |
border: none;
|
| 747 |
'>🚀 Unlimited Generation</a>
|
| 748 |
+
<a href='{task_detail_url}' target='_blank' style='
|
| 749 |
display: inline-flex;
|
| 750 |
align-items: center;
|
| 751 |
justify-content: center;
|
|
|
|
| 761 |
box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
|
| 762 |
transition: all 0.3s ease;
|
| 763 |
border: none;
|
| 764 |
+
'>💾 Download HD Image</a>
|
| 765 |
</div>
|
| 766 |
"""
|
| 767 |
|
|
|
|
| 796 |
print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
|
| 797 |
return None, t("error_processing_exception", lang).format(error=str(e)), gr.update(visible=False)
|
| 798 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 799 |
# Create Gradio interface
|
| 800 |
def create_app():
|
| 801 |
with gr.Blocks(
|
|
|
|
| 1062 |
inputs=[output_image],
|
| 1063 |
outputs=[input_image]
|
| 1064 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1065 |
|
| 1066 |
# SEO Content Section
|
| 1067 |
seo_html = gr.HTML()
|
|
|
|
| 1259 |
header_title, news_banner,
|
| 1260 |
global_tab, upload_image_header, input_image, editing_instructions_header, prompt_input, edit_button,
|
| 1261 |
editing_result_header, output_image, use_as_input_btn, status_output, prompt_examples_header,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1262 |
seo_html,
|
| 1263 |
]
|
| 1264 |
|
|
|
|
| 1294 |
use_as_input_btn: gr.update(value=t("use_as_input_button", lang)),
|
| 1295 |
status_output: gr.update(label=t("status_output_label", lang)),
|
| 1296 |
prompt_examples_header: gr.update(value=t("prompt_examples_header", lang)),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1297 |
seo_html: gr.update(value=get_seo_html(lang)),
|
| 1298 |
}
|
| 1299 |
|