File size: 14,199 Bytes
873dee2
1ddf1f6
 
 
873dee2
1ddf1f6
 
 
 
 
 
 
 
 
 
 
 
 
873dee2
1ddf1f6
 
d761612
 
 
 
 
 
 
 
1ddf1f6
 
 
6d1ca30
26041a6
1ddf1f6
 
 
 
 
 
 
 
 
 
76f7693
6a5300e
 
1ddf1f6
 
 
 
 
 
 
 
 
 
9106134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1ddf1f6
9106134
 
 
 
 
 
 
 
 
6a5300e
9106134
 
 
 
 
 
 
6a5300e
9106134
 
6a5300e
9106134
 
 
 
6a5300e
9106134
 
 
 
6a5300e
9106134
 
 
 
 
 
 
 
 
 
 
1ddf1f6
9106134
 
 
 
 
 
 
 
 
 
 
1ddf1f6
9106134
 
 
1ddf1f6
9106134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6d1ca30
9106134
 
 
 
 
6d1ca30
9106134
 
 
873dee2
9106134
 
 
 
 
 
 
 
5bc6fce
9106134
 
 
 
 
 
 
 
 
 
873dee2
9106134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96bd52c
6c3c1b3
8a73758
22b54dd
9106134
22b54dd
 
 
 
8a73758
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22b54dd
 
 
 
 
 
 
 
 
 
 
8a73758
22b54dd
 
 
 
 
6c3c1b3
22b54dd
 
 
 
 
 
 
 
3ed7d57
 
9106134
3ed7d57
 
22b54dd
8a73758
22b54dd
3ed7d57
22b54dd
6c3c1b3
9106134
 
 
1ddf1f6
9106134
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import os, sys, requests, re, time
import httpx

# =========================================================
# رفع قطعی مشکل Time Out رانر گیت‌هاب بر اساس اسپیس قدیمی
original_client_init = httpx.Client.__init__
def patched_client_init(self, *args, **kwargs):
    kwargs['timeout'] = httpx.Timeout(300.0)
    original_client_init(self, *args, **kwargs)
httpx.Client.__init__ = patched_client_init

original_async_init = httpx.AsyncClient.__init__
def patched_async_init(self, *args, **kwargs):
    kwargs['timeout'] = httpx.Timeout(300.0)
    original_async_init(self, *args, **kwargs)
httpx.AsyncClient.__init__ = patched_async_init
# =========================================================

from gradio_client import Client, handle_file

prompt = os.environ.get('PROMPT', '')
image_url_raw = os.environ.get('IMAGE_URL', '')

# تجزیه و تفکیک لینک‌ها از متغیر مشترک گیت‌هاب
urls = image_url_raw.split(',')
image_url = urls[0] if len(urls) > 0 else ""
image_url2 = urls[1] if len(urls) > 1 else ""
image_url3 = urls[2] if len(urls) > 2 else ""

run_id = os.environ.get('RUN_ID')
space_url = os.environ.get('SPACE_URL')
github_run_id = os.environ.get('GITHUB_RUN_ID')

# هماهنگی کامل با سیستم هوشمند تلاش مجدد و بررسی خطاهای شبکه
def report_failure(error_msg):
    try:
        requests.post(
            f"{space_url}/api/webhook/fail",
            json={
                "run_id": run_id,
                "error": error_msg,
                "event_type": "generate-editor",
                "client_payload": {
                    "prompt": prompt,
                    "image_url": image_url,
                    "image_url2": image_url2,
                    "image_url3": image_url3,
                    "run_id": run_id,
                    "space_url": space_url
                },
                "github_run_id": github_run_id
            },
            timeout=15
        )
    except Exception as e:
        print(f"Failed to report failure: {e}")

def check_existing_edited_image(space_url, run_id):
    """بررسی اینکه آیا تصویر ویرایش شده در تلاش قبلی آپلود شده است یا خیر"""
    for ext in ['png', 'webp', 'jpg']:
        url = f"{space_url}/static/images/{run_id}.{ext}"
        try:
            # استفاده از GET سبک با stream=True برای سازگاری کامل با تمامی سرورها
            res = requests.get(url, stream=True, timeout=10)
            if res.status_code == 200:
                return url, ext
        except:
            pass
    return None, None

print('1. Checking if edited image already exists on Docker Space...')
existing_url, existing_ext = None, None
if run_id:
    existing_url, existing_ext = check_existing_edited_image(space_url, run_id)

filename = 'output.png'
ext = 'png'

# اگر تصویر ویرایش شده از قبل موجود بود، نیازی به فرآیند ادیت مجدد نیست
if existing_url:
    print(f'   -> Edited image already exists: {existing_url}')
    print('   -> Skipping Omni Editor pipeline. Proceeding directly to CodeFormer upscaling...')
    ext = existing_ext
    filename = f'output.{ext}'
    try:
        img_req = requests.get(existing_url, timeout=60)
        with open(filename, 'wb') as f:
            f.write(img_req.content)
        print(f'   -> Downloaded existing edited image as {filename}')
    except Exception as dl_err:
        print(f'   -> Failed to download existing image: {dl_err}. Re-running Omni Editor...')
        existing_url = None

if not existing_url:
    print('1. Downloading input images from Docker Space...')
    try:
        req = requests.get(image_url, timeout=30)
        with open('input.png', 'wb') as f:
            f.write(req.content)
        print('   -> Input image 1 ready.')
    except Exception as e:
        err_str = f"Download failed for image 1: {e}"
        print(err_str)
        report_failure(err_str)
        sys.exit(1)

    # دانلود اختیاری تصویر دوم
    has_image2 = False
    if image_url2 and image_url2.strip():
        try:
            req2 = requests.get(image_url2, timeout=30)
            with open('input2.png', 'wb') as f:
                f.write(req2.content)
            has_image2 = True
            print('   -> Input image 2 ready.')
        except Exception as e:
            print(f"   -> Image 2 optional download warning (skipped): {e}")

    # دانلود اختیاری تصویر سوم
    has_image3 = False
    if image_url3 and image_url3.strip():
        try:
            req3 = requests.get(image_url3, timeout=30)
            with open('input3.png', 'wb') as f:
                f.write(req3.content)
            has_image3 = True
            print('   -> Input image 3 ready.')
        except Exception as e:
            print(f"   -> Image 3 optional download warning (skipped): {e}")

    print('2. Connecting to Omni Editor and Processing (Timeout extended to 300s)...')
    result_str = ''
    success = False

    # مکانیزم تلاش مجدد در صورت شلوغی سرور
    for attempt in range(3):
        try:
            print(f'   -> Attempt {attempt + 1} of 3...')
            client = Client('selfit-camera/omni-image-editor')
            
            # اگر فقط تصویر اول آپلود شده باشد، مثل سابق رفتار کن
            if not (has_image2 or has_image3):
                print('   -> Processing with single-image pipeline (exact same as before)...')
                result = client.predict(
                    handle_file('input.png'), 
                    prompt,
                    api_name='/edit_image_interface'
                )
            # اگر چند تصویر آپلود شده باشند، از هوک چند تصویری استفاده کن
            else:
                print('   -> Processing with multi-image pipeline (fn_index=10)...')
                result = client.predict(
                    handle_file('input.png'),
                    handle_file('input2.png') if has_image2 else None,
                    handle_file('input3.png') if has_image3 else None,
                    prompt,
                    'Auto', # نسبت تصویر پیشفرض
                    fn_index=10
                )
            
            result_str = str(result)
            if 'src=' in result_str or 'http' in result_str:
                success = True
                break
            else:
                print(f'   -> Missing valid response format: {result_str[:100]}...')
        except Exception as client_err:
            print(f'   -> Attempt {attempt + 1} failed: {client_err}')
            time.sleep(5)

    if not success:
        err_str = f"All attempts failed. Raw result: {result_str}"
        print(f'CRITICAL ERROR: {err_str}')
        report_failure(err_str)
        sys.exit(1)

    print('3. Parsing result string...')
    urls = re.findall(r'src=[\'\"]([^\'\"]+)[\'\"]', result_str)
    final_remote_url = None

    for url in urls:
        if url.startswith('http'):
            final_remote_url = url
            break
        elif url.startswith('/'):
            # برای تبدیل آدرس‌های نسبی محلی گرادیو به آدرس کامل و قابل دانلود
            final_remote_url = f"https://selfit-camera-omni-image-editor.hf.space{url}"
            break

    if not final_remote_url:
        direct_urls = re.findall(r'(https?://[^\s\'\"]+\.(?:png|jpg|jpeg|webp|gif))', result_str)
        if direct_urls:
            final_remote_url = direct_urls[0]

    if not final_remote_url:
        err_str = f"Could not find valid URL. Raw result: {result_str}"
        print(f'ERROR: {err_str}')
        report_failure(err_str)
        sys.exit(1)
        
    print(f'   -> Found Final Image URL: {final_remote_url}')

    print('4. Downloading final image from remote host...')
    try:
        img_req = requests.get(final_remote_url, timeout=60)
        
        content_type = img_req.headers.get('Content-Type', '')
        ext = 'png'
        if 'image/webp' in content_type:
            ext = 'webp'
        elif 'image/jpeg' in content_type or 'image/jpg' in content_type:
            ext = 'jpg'
            
        filename = f'output.{ext}'
        with open(filename, 'wb') as f:
            f.write(img_req.content)
        print(f'   -> Saved locally as {filename}')
    except Exception as dl_err:
        err_str = f"Failed to download processed image: {dl_err}"
        print(err_str)
        report_failure(err_str)
        sys.exit(1)

    print(f'4.2. Uploading edited image to Docker Space: {space_url}')
    try:
        with open(filename, 'rb') as f:
            res = requests.post(
                f'{space_url}/api/webhook/upload', 
                data={'run_id': run_id, 'github_run_id': github_run_id, 'ext': ext}, 
                files={'file': f},
                timeout=60
            )
        if res.status_code == 200:
            print('   -> Edited image uploaded successfully.')
        else:
            print(f'   -> Edited image upload failed: {res.status_code} - {res.text}')
    except Exception as up_err:
        print(f'   -> Edited image upload exception: {up_err}')

# =========================================================
# بخش بهبودیافته: تلاش چندجانبه برای ارتباط موفق با API اسپیس CodeFormer
print('4.5. Connecting to CodeFormer to restore and upscale (Factor=4, Fidelity=1.0)...')
cf_success = False
try:
    cf_client = Client('sczhou/CodeFormer')
    upscale_result = None
    
    # ساختار مانیتورینگ متدهای متوالی برای جلوگیری از بروز خطای api_name
    methods_to_try = [
        # روش ۱: استفاده از ایندکس صفر به همراه ساختار ۶ پارامتری (ویرایش جدید اسپیس)
        {"args": [handle_file(filename), True, True, True, 4, 1.0], "kwargs": {"fn_index": 0}},
        # روش ۲: استفاده از ایندکس صفر به همراه ساختار ۵ پارامتری (نسخه‌های پایدار قدیمی)
        {"args": [handle_file(filename), True, True, 4, 1.0], "kwargs": {"fn_index": 0}},
        # روش ۳: تلاش با ایندکس یک به همراه ساختار ۶ پارامتری
        {"args": [handle_file(filename), True, True, True, 4, 1.0], "kwargs": {"fn_index": 1}},
        # روش ۴: تلاش با ایندکس یک به همراه ساختار ۵ پارامتری
        {"args": [handle_file(filename), True, True, 4, 1.0], "kwargs": {"fn_index": 1}},
        # روش ۵: فراخوانی اسمی بدون اسلش (predict) با ساختار ۶ پارامتری
        {"args": [handle_file(filename), True, True, True, 4, 1.0], "kwargs": {"api_name": "predict"}},
        # روش ۶: فراخوانی اسمی بدون اسلش (predict) با ساختار ۵ پارامتری
        {"args": [handle_file(filename), True, True, 4, 1.0], "kwargs": {"api_name": "predict"}},
        # روش ۷: فراخوانی اسمی با اسلش (/predict) با ساختار ۶ پارامتری
        {"args": [handle_file(filename), True, True, True, 4, 1.0], "kwargs": {"api_name": "/predict"}},
        # روش ۸: فراخوانی اسمی با اسلش (/predict) با ساختار ۵ پارامتری
        {"args": [handle_file(filename), True, True, 4, 1.0], "kwargs": {"api_name": "/predict"}},
    ]
    
    for idx, method in enumerate(methods_to_try):
        try:
            print(f'   -> Trying execution path {idx + 1} of {len(methods_to_try)}...')
            upscale_result = cf_client.predict(*method["args"], **method["kwargs"])
            if upscale_result:
                print(f'   -> Success! CodeFormer handled by path {idx + 1}.')
                break
        except Exception as method_err:
            print(f'   -> Path {idx + 1} bypassed: {method_err}')
            
    upscale_path = None
    if isinstance(upscale_result, list) and len(upscale_result) > 0:
        upscale_path = upscale_result[0]
    elif isinstance(upscale_result, tuple) and len(upscale_result) > 0:
        upscale_path = upscale_result[0]
    elif isinstance(upscale_result, dict):
        upscale_path = upscale_result.get('image') or upscale_result.get('path')
    else:
        upscale_path = upscale_result

    if upscale_path and os.path.exists(str(upscale_path)):
        print(f'   -> CodeFormer Success! Output saved at {upscale_path}')
        upscale_ext = 'png'
        if str(upscale_path).endswith('.webp'):
            upscale_ext = 'webp'
        elif str(upscale_path).endswith('.jpg') or str(upscale_path).endswith('.jpeg'):
            upscale_ext = 'jpg'
            
        print(f'   -> Uploading quality-enhanced image as: {run_id}_upscaled')
        with open(upscale_path, 'rb') as f_up:
            res_up = requests.post(
                f'{space_url}/api/webhook/upload', 
                data={'run_id': f"{run_id}_upscaled", 'github_run_id': github_run_id, 'ext': upscale_ext}, 
                files={'file': f_up},
                timeout=60
            )
        if res_up.status_code == 200:
            print('   -> Enhanced image uploaded successfully.')
            cf_success = True
        else:
            print(f'   -> Enhanced image upload failed: {res_up.status_code} - {res_up.text}')
    else:
        print('   -> Warning: CodeFormer did not return a valid file path.')
except Exception as cf_err:
    print(f'   -> Warning: CodeFormer process bypassed or failed: {cf_err}')
# =========================================================

if not cf_success:
    err_str = "CodeFormer upscaling failed or was bypassed due to ZeroGPU/Gradio connection limits."
    print(f'CRITICAL WARNING: {err_str}')
    report_failure(err_str)
    sys.exit(1) # بازگرداندن وضعیت خطا به رانر گیت‌هاب تا فرآیند تلاش مجدد را تا ۲۰ بار تکرار کند

print('6. SUCCESS! Process finished successfully.')