selfit-camera commited on
Commit
b15d785
·
1 Parent(s): 243318e
Files changed (1) hide show
  1. app.py +6 -550
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, process_local_image_edit, download_and_check_result_nsfw
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 = 5 # Max usage for restricted countries
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 image-to-video URL with input image, end image, and prompt
728
- from urllib.parse import quote
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
  '>&#128640; Unlimited Generation</a>
751
- <a href='{image_to_video_url}' target='_blank' style='
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
- '>&#127909; Turn Image to Video</a>
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
- '>&#128640; 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
- '>&#128640; 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
- '>&#127909; 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
  '>&#128640; 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
+ '>&#128190; 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