Sada8888 commited on
Commit
1a9e883
·
verified ·
1 Parent(s): cd5d2e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +865 -15
app.py CHANGED
@@ -97,6 +97,850 @@ def create_error_image(run_id, message):
97
  except Exception:
98
  pass
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def init_db():
101
  conn = sqlite3.connect(DB_PATH)
102
  c = conn.cursor()
@@ -204,7 +1048,7 @@ def is_today(created_at_str):
204
  except Exception:
205
  return False
206
 
207
- # تابع ارسال اکشن داینامیک به گیت‌هاب بر اساس فایل‌های درون پوشه yml
208
  def dispatch_github_workflow(event_type, client_payload):
209
  acc = get_next_account()
210
  if not acc:
@@ -219,13 +1063,14 @@ def dispatch_github_workflow(event_type, client_payload):
219
  if os.path.exists(script_path):
220
  with open(script_path, "r", encoding="utf-8") as f:
221
  script_content = f.read()
222
-
223
  # تزریق داینامیک متغیرهایی که در یمل گیت‌هاب قرار ندارند برای تولید صدا
224
  if event_type == "generate-audio":
225
- audio_type = client_payload.get('audio_type', 'text')
226
- negative_prompt = client_payload.get('negative_prompt', 'music')
227
- script_content = script_content.replace("___AUDIO_TYPE___", audio_type)
228
- script_content = script_content.replace("___NEGATIVE_PROMPT___", negative_prompt)
 
229
 
230
  if "data" in client_payload:
231
  client_payload["data"]["script"] = script_content
@@ -359,7 +1204,7 @@ def cancel_delete_active_actions():
359
  "deleted_count": deleted_count
360
  })
361
 
362
- # وب‌هوک مدیریت دریافت خطا و اجرای تلاش مجدد تا سقف ۵ بار
363
  @app.route('/api/webhook/fail', methods=['POST'])
364
  def webhook_fail():
365
  data = request.get_json()
@@ -463,6 +1308,7 @@ def generate():
463
  return jsonify({"status": "error", "message": "متن ورودی شامل کلمات نامناسب است."}), 400
464
 
465
  try:
 
466
  english_prompt = GoogleTranslator(source='auto', target='en').translate(persian_prompt)
467
  print(f"[GOOGLE TRANSLATE SUCCESS] Original: {persian_prompt} -> Translated: {english_prompt}")
468
  except Exception as e:
@@ -740,14 +1586,16 @@ def generate_audio():
740
  file_url = f"{SPACE_URL}/static/images/{filename}"
741
 
742
  client_payload = {
743
- "prompt": english_prompt,
744
- "negative_prompt": english_negative,
745
- "duration": duration,
746
- "seed": seed,
747
- "audio_type": audio_type,
748
- "file_url": file_url,
749
- "run_id": run_id,
750
- "space_url": SPACE_URL
 
 
751
  }
752
 
753
  r = dispatch_github_workflow("generate-audio", client_payload)
@@ -796,6 +1644,7 @@ def worker_poll():
796
 
797
  # ==========================================
798
 
 
799
  # --- API میکس ویدیوها سمت سرور ---
800
  @app.route('/api/merge-videos', methods=['POST'])
801
  def merge_videos():
@@ -1056,6 +1905,7 @@ def use_edit_credit():
1056
  conn.close()
1057
  return jsonify({"status": "success", "credits_remaining": credits_remaining})
1058
 
 
1059
  # --- دریافت و ارسال فایل ---
1060
  @app.route('/api/webhook/upload', methods=['POST'])
1061
  def webhook_upload():
 
97
  except Exception:
98
  pass
99
 
100
+ # --- ایجاد خودکار اسکریپت‌های مورد نیاز درون پوشه yml در بدو استارت آپ ---
101
+ def write_default_scripts():
102
+ scripts = {
103
+ "generate-flux.py": """
104
+ import os, sys, random, requests
105
+ from gradio_client import Client
106
+
107
+ def get_env_int(name, default=0):
108
+ val = os.environ.get(name, '')
109
+ try:
110
+ return int(val) if val.strip() else default
111
+ except ValueError:
112
+ return default
113
+
114
+ prompt = os.environ.get('PROMPT', '')
115
+ width = get_env_int('WIDTH', 1024)
116
+ height = get_env_int('HEIGHT', 1024)
117
+ run_id = os.environ.get('RUN_ID')
118
+ space_url = os.environ.get('SPACE_URL')
119
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
120
+
121
+ def report_failure(error_msg):
122
+ try:
123
+ requests.post(
124
+ f"{space_url}/api/webhook/fail",
125
+ json={
126
+ "run_id": run_id,
127
+ "error": error_msg,
128
+ "event_type": "generate-flux",
129
+ "client_payload": {
130
+ "prompt": prompt,
131
+ "width": width,
132
+ "height": height,
133
+ "run_id": run_id,
134
+ "space_url": space_url
135
+ },
136
+ "github_run_id": github_run_id
137
+ },
138
+ timeout=15
139
+ )
140
+ except Exception as e:
141
+ print(f"Failed to report failure: {e}")
142
+
143
+ print('1. Connecting to Flux...')
144
+ try:
145
+ client = Client('black-forest-labs/FLUX.1-dev')
146
+ seed = random.randint(1, 2147483647)
147
+
148
+ result = client.predict(prompt, seed, True, width, height, 3.5, 28, api_name='/infer')
149
+ image_path = result[0] if isinstance(result, (tuple, list)) else result
150
+
151
+ print(f'2. Uploading back to Docker Space...')
152
+ with open(image_path, 'rb') as f:
153
+ res = requests.post(f'{space_url}/api/webhook/upload', data={'run_id': run_id, 'github_run_id': github_run_id}, files={'file': f})
154
+
155
+ if res.status_code == 200:
156
+ print('3. SUCCESS!')
157
+ else:
158
+ sys.exit(1)
159
+
160
+ except Exception as e:
161
+ err_str = str(e)
162
+ print(f'ERROR: {err_str}')
163
+ report_failure(err_str)
164
+ sys.exit(1)
165
+ """,
166
+ "generate-flux2.py": """
167
+ import os, sys, random, requests, json
168
+ from gradio_client import Client, handle_file
169
+
170
+ def get_env_int(name, default=0):
171
+ val = os.environ.get(name, '')
172
+ try:
173
+ return int(val) if val.strip() else default
174
+ except ValueError:
175
+ return default
176
+
177
+ def get_env_float(name, default=0.0):
178
+ val = os.environ.get(name, '')
179
+ try:
180
+ return float(val) if val.strip() else default
181
+ except ValueError:
182
+ return default
183
+
184
+ prompt = os.environ.get('PROMPT', '')
185
+ image_urls_raw = os.environ.get('IMAGE_URLS', '[]')
186
+ width = get_env_int('WIDTH', 1024)
187
+ height = get_env_int('HEIGHT', 1024)
188
+ steps = get_env_int('STEPS', 30)
189
+ guidance = get_env_float('GUIDANCE', 4.0)
190
+ seed = get_env_int('SEED', 0)
191
+ randomize_seed = os.environ.get('RANDOMIZE_SEED', 'true').lower() == 'true'
192
+ prompt_upsampling = os.environ.get('PROMPT_UPSAMPLING', 'true').lower() == 'true'
193
+ run_id = os.environ.get('RUN_ID')
194
+ space_url = os.environ.get('SPACE_URL')
195
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
196
+ hf_token = os.environ.get('HF_TOKEN', '')
197
+
198
+ print(f'Starting direct Flux 2 generation for run_id: {run_id}')
199
+
200
+ print('1. Parsing and downloading images...')
201
+ image_urls = []
202
+ if image_urls_raw:
203
+ try:
204
+ cleaned_json = image_urls_raw.replace('null', '').strip()
205
+ if cleaned_json and cleaned_json != '[]':
206
+ image_urls = json.loads(cleaned_json)
207
+ except Exception as e:
208
+ print(f'JSON parsing error: {e}')
209
+
210
+ def report_failure(error_msg):
211
+ try:
212
+ requests.post(
213
+ f"{space_url}/api/webhook/fail",
214
+ json={
215
+ "run_id": run_id,
216
+ "error": error_msg,
217
+ "event_type": "generate-flux2",
218
+ "client_payload": {
219
+ "data": {
220
+ "prompt": prompt,
221
+ "image_urls": image_urls,
222
+ "width": width,
223
+ "height": height,
224
+ "steps": steps,
225
+ "guidance": guidance,
226
+ "seed": seed,
227
+ "randomize_seed": randomize_seed,
228
+ "prompt_upsampling": prompt_upsampling,
229
+ "run_id": run_id,
230
+ "space_url": space_url,
231
+ "hf_token": hf_token
232
+ }
233
+ },
234
+ "github_run_id": github_run_id
235
+ },
236
+ timeout=15
237
+ )
238
+ except Exception as e:
239
+ print(f"Failed to report failure: {e}")
240
+
241
+ files_to_send = []
242
+ for idx, url in enumerate(image_urls):
243
+ if url:
244
+ print(f'Downloading input image {idx}: {url}')
245
+ ext = url.split('.')[-1] if '.' in url else 'png'
246
+ local_filename = f'input_{idx}.{ext}'
247
+ try:
248
+ req = requests.get(url, timeout=30)
249
+ with open(local_filename, 'wb') as f:
250
+ f.write(req.content)
251
+ files_to_send.append({'image': handle_file(local_filename), 'caption': None})
252
+ except Exception as e:
253
+ print(f'Failed to download {url}: {e}')
254
+
255
+ print('2. Connecting to FLUX.2-dev Space...')
256
+ try:
257
+ if hf_token:
258
+ client = Client('black-forest-labs/FLUX.2-dev', token=hf_token)
259
+ else:
260
+ client = Client('black-forest-labs/FLUX.2-dev')
261
+
262
+ if randomize_seed:
263
+ seed = random.randint(1, 2147483647)
264
+
265
+ print(f'Predicting with seed={seed}, width={width}, height={height}, steps={steps}, guidance={guidance}, upsampling={prompt_upsampling}...')
266
+ result = client.predict(
267
+ prompt,
268
+ files_to_send,
269
+ seed,
270
+ randomize_seed,
271
+ width,
272
+ height,
273
+ steps,
274
+ guidance,
275
+ prompt_upsampling,
276
+ api_name='/infer'
277
+ )
278
+
279
+ image_path = result[0] if isinstance(result, (tuple, list)) else result
280
+ seed_used = result[1] if (isinstance(result, (tuple, list)) and len(result) > 1) else seed
281
+
282
+ print('3. Uploading metadata seed back...')
283
+ with open('seed.txt', 'w') as sf:
284
+ sf.write(str(seed_used))
285
+ requests.post(
286
+ f'{space_url}/api/webhook/upload',
287
+ data={'run_id': f'{run_id}_seed', 'github_run_id': github_run_id, 'ext': 'txt'},
288
+ files={'file': open('seed.txt', 'rb')}
289
+ )
290
+
291
+ print('4. Uploading generated image back...')
292
+ with open(image_path, 'rb') as f:
293
+ upload_res = requests.post(
294
+ f'{space_url}/api/webhook/upload',
295
+ data={'run_id': run_id, 'github_run_id': github_run_id},
296
+ files={'file': f}
297
+ )
298
+
299
+ if upload_res.status_code == 200:
300
+ print('SUCCESS!')
301
+ else:
302
+ print(f'Upload failed with status {upload_res.status_code}: {upload_res.text}')
303
+ sys.exit(1)
304
+
305
+ except Exception as e:
306
+ err_str = str(e)
307
+ print(f'Generation error: {err_str}')
308
+ report_failure(err_str)
309
+ sys.exit(1)
310
+ """,
311
+ "generate-cartoon.py": """
312
+ import os, sys, random, requests
313
+ from gradio_client import Client
314
+
315
+ def get_env_int(name, default=0):
316
+ val = os.environ.get(name, '')
317
+ try:
318
+ return int(val) if val.strip() else default
319
+ except ValueError:
320
+ return default
321
+
322
+ prompt = os.environ.get('PROMPT', '')
323
+ width = get_env_int('WIDTH', 1024)
324
+ height = get_env_int('HEIGHT', 1024)
325
+ run_id = os.environ.get('RUN_ID')
326
+ space_url = os.environ.get('SPACE_URL')
327
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
328
+
329
+ def report_failure(error_msg):
330
+ try:
331
+ requests.post(
332
+ f"{space_url}/api/webhook/fail",
333
+ json={
334
+ "run_id": run_id,
335
+ "error": error_msg,
336
+ "event_type": "generate-cartoon",
337
+ "client_payload": {
338
+ "prompt": prompt,
339
+ "width": width,
340
+ "height": height,
341
+ "run_id": run_id,
342
+ "space_url": space_url
343
+ },
344
+ "github_run_id": github_run_id
345
+ },
346
+ timeout=15
347
+ )
348
+ except Exception as e:
349
+ print(f"Failed to report failure: {e}")
350
+
351
+ print('1. Connecting to LoRA DLC Space...')
352
+ try:
353
+ client = Client('prithivMLmods/FLUX-LoRA-DLC')
354
+ print('2. Loading Cartoon Model (Long Toons)...')
355
+ client.predict('prithivMLmods/Flux-Long-Toon-LoRA', api_name='/add_custom_lora')
356
+
357
+ print('3. Generating Cartoon Image...')
358
+ result = client.predict(
359
+ prompt=f'Long toons {prompt}',
360
+ image_input=None,
361
+ cfg_scale=3.5,
362
+ steps=28,
363
+ randomize_seed=True,
364
+ seed=random.randint(1, 2147483647),
365
+ width=width,
366
+ height=height,
367
+ lora_scale=0.8,
368
+ api_name='/run_lora'
369
+ )
370
+ image_path = result[0] if isinstance(result, (tuple, list)) else result
371
+
372
+ print('4. Uploading back to Docker Space...')
373
+ with open(image_path, 'rb') as f:
374
+ res = requests.post(f'{space_url}/api/webhook/upload',
375
+ data={'run_id': run_id, 'github_run_id': github_run_id},
376
+ files={'file': f})
377
+ if res.status_code == 200:
378
+ print('5. SUCCESS!')
379
+ else:
380
+ sys.exit(1)
381
+ except Exception as e:
382
+ err_str = str(e)
383
+ print(f'CRITICAL ERROR: {err_str}')
384
+ report_failure(err_str)
385
+ sys.exit(1)
386
+ """,
387
+ "generate-midjourney.py": """
388
+ import os, sys, random, requests
389
+ from gradio_client import Client
390
+
391
+ def get_env_int(name, default=0):
392
+ val = os.environ.get(name, '')
393
+ try:
394
+ return int(val) if val.strip() else default
395
+ except ValueError:
396
+ return default
397
+
398
+ raw_prompt = os.environ.get('PROMPT', '')
399
+ final_prompt = f'MJ v6 {raw_prompt}'
400
+ width = get_env_int('WIDTH', 1024)
401
+ height = get_env_int('HEIGHT', 1024)
402
+ run_id = os.environ.get('RUN_ID')
403
+ space_url = os.environ.get('SPACE_URL')
404
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
405
+
406
+ def report_failure(error_msg):
407
+ try:
408
+ requests.post(
409
+ f"{space_url}/api/webhook/fail",
410
+ json={
411
+ "run_id": run_id,
412
+ "error": error_msg,
413
+ "event_type": "generate-midjourney",
414
+ "client_payload": {
415
+ "prompt": raw_prompt,
416
+ "width": width,
417
+ "height": height,
418
+ "run_id": run_id,
419
+ "space_url": space_url
420
+ },
421
+ "github_run_id": github_run_id
422
+ },
423
+ timeout=15
424
+ )
425
+ except Exception as e:
426
+ print(f"Failed to report failure: {e}")
427
+
428
+ print('1. Connecting to LoRA DLC Space...')
429
+ try:
430
+ client = Client('prithivMLmods/FLUX-LoRA-DLC')
431
+ print('2. Loading Midjourney Model...')
432
+ client.predict('strangerzonehf/Flux-Midjourney-Mix2-LoRA', api_name='/add_custom_lora')
433
+
434
+ print(f'3. Generating Image... Prompt: {final_prompt[:50]}...')
435
+ result = client.predict(
436
+ prompt=final_prompt,
437
+ image_input=None,
438
+ cfg_scale=3.5,
439
+ steps=28,
440
+ randomize_seed=True,
441
+ seed=random.randint(1, 2147483647),
442
+ width=width,
443
+ height=height,
444
+ lora_scale=0.8,
445
+ api_name='/run_lora'
446
+ )
447
+ image_path = result[0] if isinstance(result, (tuple, list)) else result
448
+
449
+ print('4. Uploading back to Docker Space...')
450
+ with open(image_path, 'rb') as f:
451
+ res = requests.post(f'{space_url}/api/webhook/upload',
452
+ data={'run_id': run_id, 'github_run_id': github_run_id},
453
+ files={'file': f})
454
+ if res.status_code == 200:
455
+ print('5. SUCCESS!')
456
+ else:
457
+ sys.exit(1)
458
+ except Exception as e:
459
+ err_str = str(e)
460
+ print(f'CRITICAL ERROR: {err_str}')
461
+ report_failure(err_str)
462
+ sys.exit(1)
463
+ """,
464
+ "chat-gemma.py": """
465
+ import os, sys, requests
466
+ from gradio_client import Client, handle_file
467
+
468
+ prompt = os.environ.get('PROMPT', '')
469
+ file_url = os.environ.get('FILE_URL', '')
470
+ run_id = os.environ.get('RUN_ID')
471
+ space_url = os.environ.get('SPACE_URL')
472
+ gh_run_id = os.environ.get('GITHUB_RUN_ID')
473
+
474
+ files_to_send = []
475
+ if file_url:
476
+ print(f'Downloading attachment: {file_url}')
477
+ ext = file_url.split('.')[-1] if '.' in file_url else 'bin'
478
+ local_filename = f'attachment.{ext}'
479
+ try:
480
+ req = requests.get(file_url, timeout=30)
481
+ with open(local_filename, 'wb') as f:
482
+ f.write(req.content)
483
+ files_to_send.append(handle_file(local_filename))
484
+ except Exception as e:
485
+ print(f'Failed to download file: {e}')
486
+
487
+ print('Connecting to Gemma-4 Space...')
488
+ bot_reply = ''
489
+ try:
490
+ client = Client('huggingface-projects/gemma-4-e4b-it')
491
+ message_payload = {'text': prompt, 'files': files_to_send}
492
+ result = client.predict(
493
+ message=message_payload,
494
+ thinking=False,
495
+ max_new_tokens=4000,
496
+ max_soft_tokens=1120,
497
+ system_prompt='شما یک دستیار هوش مصنوعی بسیار باهوش، مفید و مودب به زبان فارسی هستید.',
498
+ api_name='/generate'
499
+ )
500
+ bot_reply = result[0] if isinstance(result, (list, tuple)) else result
501
+ if isinstance(bot_reply, dict):
502
+ bot_reply = bot_reply.get('text', str(bot_reply))
503
+ except Exception as e:
504
+ err_str = str(e)
505
+ print(f'Generation Error: {err_str}')
506
+ if 'ZeroGPU quota' in err_str or 'quota' in err_str.lower():
507
+ bot_reply = '⚠️ **پردازش متوقف شد:**\\nدرخواست شما طولانی و پرحجم است (پردازش فایل‌های صوتی یا حجیم نیازمند منابع بالایی است و سهمیه سرور موقتاً پر شده است).\\n\\nلطفاً چند ساعت دیگر امتحان کنید یا درخواست و فایل سبک‌تری ارسال نمایید.'
508
+ else:
509
+ bot_reply = f'⚠️ خطای سرور پردازش: {err_str}'
510
+
511
+ print('Uploading response back to Docker Space...')
512
+ try:
513
+ with open('response.txt', 'w', encoding='utf-8') as f:
514
+ f.write(bot_reply)
515
+ with open('response.txt', 'rb') as f:
516
+ res = requests.post(f'{space_url}/api/webhook/upload',
517
+ data={'run_id': run_id, 'github_run_id': gh_run_id, 'ext': 'txt'},
518
+ files={'file': f})
519
+ if res.status_code == 200:
520
+ print('SUCCESS!')
521
+ else:
522
+ sys.exit(1)
523
+ except Exception as e:
524
+ sys.exit(1)
525
+ """,
526
+ "generate-editor.py": """
527
+ import os, sys, requests, re, time
528
+
529
+ # =========================================================
530
+ # رفع قطعی مشکل The read operation timed out
531
+ # تنظیم دستی زمان انتظار به 5 دقیقه (300 ثانیه) مانند مرورگر
532
+ import httpx
533
+ original_client_init = httpx.Client.__init__
534
+ def patched_client_init(self, *args, **kwargs):
535
+ kwargs['timeout'] = httpx.Timeout(300.0)
536
+ original_client_init(self, *args, **kwargs)
537
+ httpx.Client.__init__ = patched_client_init
538
+
539
+ original_async_init = httpx.AsyncClient.__init__
540
+ def patched_async_init(self, *args, **kwargs):
541
+ kwargs['timeout'] = httpx.Timeout(300.0)
542
+ original_async_init(self, *args, **kwargs)
543
+ httpx.AsyncClient.__init__ = patched_async_init
544
+ # =========================================================
545
+
546
+ from gradio_client import Client, handle_file
547
+
548
+ prompt = os.environ.get('PROMPT', '')
549
+ image_url = os.environ.get('IMAGE_URL', '')
550
+ run_id = os.environ.get('RUN_ID')
551
+ space_url = os.environ.get('SPACE_URL')
552
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
553
+
554
+ def report_failure(error_msg):
555
+ try:
556
+ requests.post(
557
+ f"{space_url}/api/webhook/fail",
558
+ json={
559
+ "run_id": run_id,
560
+ "error": error_msg,
561
+ "event_type": "generate-editor",
562
+ "client_payload": {
563
+ "prompt": prompt,
564
+ "image_url": image_url,
565
+ "run_id": run_id,
566
+ "space_url": space_url
567
+ },
568
+ "github_run_id": github_run_id
569
+ },
570
+ timeout=15
571
+ )
572
+ except Exception as e:
573
+ print(f"Failed to report failure: {e}")
574
+
575
+ print('1. Downloading input image from Docker Space...')
576
+ try:
577
+ req = requests.get(image_url, timeout=30)
578
+ with open('input.png', 'wb') as f:
579
+ f.write(req.content)
580
+ print(' -> Input image ready.')
581
+ except Exception as e:
582
+ print(f'Download failed: {e}')
583
+ sys.exit(1)
584
+
585
+ print('2. Connecting to Omni Editor and Processing (Timeout extended to 300s)...')
586
+ result_str = ''
587
+ success = False
588
+
589
+ for attempt in range(3):
590
+ try:
591
+ print(f' -> Attempt {attempt + 1} of 3...')
592
+ client = Client('selfit-camera/omni-image-editor')
593
+ result = client.predict(
594
+ handle_file('input.png'),
595
+ prompt,
596
+ api_name='/edit_image_interface'
597
+ )
598
+ result_str = str(result)
599
+ if 'src=' in result_str or 'http' in result_str:
600
+ success = True
601
+ break
602
+ else:
603
+ print(f' -> Missing valid response format: {result_str[:100]}...')
604
+ except Exception as client_err:
605
+ print(f' -> Attempt {attempt + 1} failed: {client_err}')
606
+ time.sleep(5)
607
+
608
+ if not success:
609
+ print(f'CRITICAL ERROR: All attempts failed. Raw result: {result_str}')
610
+ sys.exit(1)
611
+
612
+ print('3. Parsing result string...')
613
+ urls = re.findall(r'src=[\'\"]([^\'\"]+)[\'\"]', result_str)
614
+ final_remote_url = None
615
+ for url in urls:
616
+ if url.startswith('http'):
617
+ final_remote_url = url
618
+ break
619
+
620
+ if not final_remote_url:
621
+ direct_urls = re.findall(r'(https?://[^\s\'\"]+\.(?:png|jpg|jpeg|webp|gif))', result_str)
622
+ if direct_urls:
623
+ final_remote_url = direct_urls[0]
624
+
625
+ if not final_remote_url:
626
+ print(f'ERROR: Could not find valid URL. Raw result: {result_str}')
627
+ sys.exit(1)
628
+
629
+ print(f' -> Found Final Image URL: {final_remote_url}')
630
+
631
+ print('4. Downloading final image from remote host...')
632
+ try:
633
+ img_req = requests.get(final_remote_url, timeout=60)
634
+ content_type = img_req.headers.get('Content-Type', '')
635
+ ext = 'png'
636
+ if 'image/webp' in content_type:
637
+ ext = 'webp'
638
+ elif 'image/jpeg' in content_type or 'image/jpg' in content_type:
639
+ ext = 'jpg'
640
+
641
+ filename = f'output.{ext}'
642
+ with open(filename, 'wb') as f:
643
+ f.write(img_req.content)
644
+ print(f' -> Saved locally as {filename}')
645
+ except Exception as dl_err:
646
+ print(f'Failed to download processed image: {dl_err}')
647
+ sys.exit(1)
648
+
649
+ print(f'5. Uploading back to Docker Space: {space_url}')
650
+ try:
651
+ with open(filename, 'rb') as f:
652
+ res = requests.post(
653
+ f'{space_url}/api/webhook/upload',
654
+ data={'run_id': run_id, 'github_run_id': github_run_id, 'ext': ext},
655
+ files={'file': f},
656
+ timeout=60
657
+ )
658
+ if res.status_code == 200:
659
+ print('6. SUCCESS!')
660
+ else:
661
+ sys.exit(1)
662
+ except Exception as e:
663
+ err_str = str(e)
664
+ report_failure(err_str)
665
+ sys.exit(1)
666
+ """,
667
+ "generate-video.py": """
668
+ import os, sys, random, requests, time
669
+ from gradio_client import Client, handle_file
670
+ from PIL import Image, ImageOps
671
+
672
+ prompt = os.environ.get('PROMPT', '').strip()
673
+ if not prompt:
674
+ prompt = 'make this image come alive, cinematic motion, smooth animation'
675
+ else:
676
+ prompt = f'{prompt}, cinematic motion, smooth animation, high quality'
677
+
678
+ negative_prompt = '色调艳丽, 过曝, 静态, 细节模糊不清, 字幕, 风格, 作品, 画作, 画面, 静止, 整体发灰, 最差质量, 低质量, JPEG压缩残留, 丑陋의, 残缺의, 多余的手指, 画得不好的手部, 画得不好的脸部, 畸形の, 毁容의, 形态畸形的肢体, 手指融合, 静止不动的画面, 杂乱의 背景, 三条腿, 背景人很多, 倒着走'
679
+
680
+ image_url = os.environ.get('IMAGE_URL', '')
681
+ run_id = os.environ.get('RUN_ID')
682
+ space_url = os.environ.get('SPACE_URL')
683
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
684
+
685
+ try:
686
+ duration = float(os.environ.get('DURATION', 5.0))
687
+ except Exception:
688
+ duration = 5.0
689
+
690
+ def report_failure(error_msg):
691
+ try:
692
+ requests.post(
693
+ f"{space_url}/api/webhook/fail",
694
+ json={
695
+ "run_id": run_id,
696
+ "error": error_msg,
697
+ "event_type": "generate-video",
698
+ "client_payload": {
699
+ "prompt": prompt,
700
+ "duration": duration,
701
+ "image_url": image_url,
702
+ "run_id": run_id,
703
+ "space_url": space_url
704
+ },
705
+ "github_run_id": github_run_id
706
+ },
707
+ timeout=15
708
+ )
709
+ except Exception as e:
710
+ print(f"Failed to report failure: {e}")
711
+
712
+ print('1. Downloading input image from Docker Space...')
713
+ try:
714
+ req = requests.get(image_url, timeout=30)
715
+ with open('input_raw.png', 'wb') as f:
716
+ f.write(req.content)
717
+ except Exception as e:
718
+ print(f'Download failed: {e}')
719
+ sys.exit(1)
720
+
721
+ print('2. Analyzing image dimensions to prevent stretching...')
722
+ img = Image.open('input_raw.png').convert('RGB')
723
+ orig_w, orig_h = img.size
724
+ ratio = orig_w / orig_h
725
+
726
+ if ratio > 1.2:
727
+ target_w, target_h = 832, 480
728
+ print(' -> Format: Landscape (16:9)')
729
+ elif ratio < 0.8:
730
+ target_w, target_h = 480, 832
731
+ print(' -> Format: Portrait (9:16)')
732
+ else:
733
+ target_w, target_h = 480, 480
734
+ print(' -> Format: Square (1:1)')
735
+
736
+ img_resized = ImageOps.fit(img, (target_w, target_h), Image.Resampling.LANCZOS)
737
+ img_resized.save('input.png')
738
+ print(f' -> Image perfectly resized to {target_w}x{target_h}')
739
+
740
+ print('3. Connecting to AI Video Space...')
741
+ video_path = None
742
+
743
+ space_id = 'zerogpu-aoti/wan2-2-fp8da-aoti-faster'
744
+ print(f' -> Loading Space: {space_id}')
745
+ try:
746
+ client = Client(space_id)
747
+ except Exception as e:
748
+ print(f'CRITICAL ERROR: Could not load space. {e}')
749
+ sys.exit(1)
750
+
751
+ max_attempts = 4
752
+ for attempt in range(max_attempts):
753
+ seed = random.randint(1, 2147483647)
754
+ print(f' -> Attempt {attempt + 1} of {max_attempts} with seed {seed} and duration {duration}s...')
755
+
756
+ try:
757
+ result = client.predict(
758
+ handle_file('input.png'), # [0] عکس ورودی
759
+ prompt, # [1] پرامپت مثبت
760
+ 6, # [2] مقدار ثابت
761
+ negative_prompt, # [3] پرامپت منفی
762
+ duration, # [4] زمان انتخابی کاربر
763
+ 1, # [5] مقدار ثابت
764
+ 1, # [6] مقدار ثابت
765
+ seed, # [7] هسته پردازش
766
+ True, # [8] رندوم کردن Seed
767
+ fn_index=0
768
+ )
769
+
770
+ if isinstance(result, list) and len(result) > 0:
771
+ video_path = result[0].get('video') or result[0].get('path') if isinstance(result[0], dict) else result[0]
772
+ elif isinstance(result, tuple) and len(result) > 0:
773
+ video_path = result[0]
774
+ elif isinstance(result, dict):
775
+ video_path = result.get('video') or result.get('path')
776
+ else:
777
+ video_path = result
778
+
779
+ if video_path and os.path.exists(str(video_path)):
780
+ print(' -> Success! Video generated.')
781
+ break
782
+
783
+ except Exception as e:
784
+ err_msg = str(e)
785
+ print(f' -> Failed on attempt {attempt + 1}: {err_msg}')
786
+ time.sleep(5)
787
+
788
+ if not video_path or not os.path.exists(str(video_path)):
789
+ err_str = "Video generation failed on all attempts"
790
+ report_failure(err_str)
791
+ sys.exit(1)
792
+
793
+ print('4. Uploading back to your Docker Space...')
794
+ try:
795
+ with open(video_path, 'rb') as f:
796
+ res = requests.post(f'{space_url}/api/webhook/upload',
797
+ data={'run_id': run_id, 'github_run_id': github_run_id, 'ext': 'mp4'},
798
+ files={'file': f})
799
+ if res.status_code == 200:
800
+ print('5. SUCCESS!')
801
+ else:
802
+ sys.exit(1)
803
+ except Exception as e:
804
+ err_str = str(e)
805
+ report_failure(err_str)
806
+ sys.exit(1)
807
+ """,
808
+ "generate-audio.py": """
809
+ import os, sys, requests, time
810
+ from gradio_client import Client, handle_file
811
+
812
+ # --- Variables injected by Flask ---
813
+ AUDIO_TYPE = '___AUDIO_TYPE___'
814
+ NEGATIVE_PROMPT = '''___NEGATIVE_PROMPT___'''
815
+
816
+ prompt = os.environ.get('PROMPT', '')
817
+ try: seed = int(os.environ.get('SEED', '-1'))
818
+ except: seed = -1
819
+ try: duration = float(os.environ.get('DURATION', '8'))
820
+ except: duration = 8.0
821
+
822
+ file_url = os.environ.get('FILE_URL', '')
823
+ run_id = os.environ.get('RUN_ID')
824
+ space_url = os.environ.get('SPACE_URL')
825
+ github_run_id = os.environ.get('GITHUB_RUN_ID')
826
+
827
+ def report_failure(error_msg):
828
+ try:
829
+ requests.post(
830
+ f"{space_url}/api/webhook/fail",
831
+ json={
832
+ "run_id": run_id,
833
+ "error": error_msg,
834
+ "event_type": "generate-audio",
835
+ "client_payload": {
836
+ "data": {
837
+ "prompt": prompt,
838
+ "duration": duration,
839
+ "seed": seed,
840
+ "audio_type": AUDIO_TYPE,
841
+ "negative_prompt": NEGATIVE_PROMPT,
842
+ "file_url": file_url,
843
+ "run_id": run_id,
844
+ "space_url": space_url
845
+ }
846
+ },
847
+ "github_run_id": github_run_id
848
+ },
849
+ timeout=15
850
+ )
851
+ except Exception as e:
852
+ print(f"Failed to report failure: {e}")
853
+
854
+ try:
855
+ print('1. Connecting to Audio Space...')
856
+ client = Client("hkchengrex/MMAudio")
857
+
858
+ if AUDIO_TYPE == 'video':
859
+ print('2. Downloading video for audio generation...')
860
+ req = requests.get(file_url, timeout=30)
861
+ ext = file_url.split('.')[-1] if '.' in file_url else 'mp4'
862
+ local_vid = f'input.{ext}'
863
+ with open(local_vid, 'wb') as f:
864
+ f.write(req.content)
865
+
866
+ print('3. Processing Video-to-Audio...')
867
+ result = client.predict(
868
+ video=handle_file(local_vid),
869
+ prompt=prompt,
870
+ negative_prompt=NEGATIVE_PROMPT,
871
+ seed=seed,
872
+ num_steps=25,
873
+ cfg_strength=4.5,
874
+ duration=duration,
875
+ api_name="/video_to_audio"
876
+ )
877
+ else:
878
+ print('2. Processing Text-to-Audio...')
879
+ result = client.predict(
880
+ prompt=prompt,
881
+ negative_prompt=NEGATIVE_PROMPT,
882
+ seed=seed,
883
+ num_steps=25,
884
+ cfg_strength=4.5,
885
+ duration=duration,
886
+ api_name="/text_to_audio"
887
+ )
888
+
889
+ print('4. Preparing File Data...')
890
+ audio_path = None
891
+ if isinstance(result, list) and len(result) > 0:
892
+ audio_path = result[0].get('video') or result[0].get('path') or result[0].get('url') if isinstance(result[0], dict) else result[0]
893
+ elif isinstance(result, tuple) and len(result) > 0:
894
+ audio_path = result[0]
895
+ elif isinstance(result, dict):
896
+ audio_path = result.get('video') or result.get('path') or result.get('url')
897
+ else:
898
+ audio_path = result
899
+
900
+ ext = 'mp4'
901
+ if str(audio_path).endswith('.wav'): ext = 'wav'
902
+ if str(audio_path).endswith('.mp3'): ext = 'mp3'
903
+
904
+ print('5. Uploading back to Space...')
905
+ with open(audio_path, 'rb') as f:
906
+ res = requests.post(
907
+ f'{space_url}/api/webhook/upload',
908
+ data={'run_id': run_id, 'github_run_id': github_run_id, 'ext': ext},
909
+ files={'file': f}
910
+ )
911
+
912
+ if res.status_code == 200:
913
+ print('SUCCESS!')
914
+ else:
915
+ sys.exit(1)
916
+
917
+ except Exception as e:
918
+ err_str = str(e)
919
+ print(f'Generation error: {err_str}')
920
+ report_failure(err_str)
921
+ sys.exit(1)
922
+ """
923
+ }
924
+ for name, content in scripts.items():
925
+ path = os.path.join("yml", name)
926
+
927
+ # مانیتورینگ تگ شکست برای بازنویسی و اعمال ساختار پویا و دفاعی جدید روی تمام فایل‌های پایتون یمل
928
+ if os.path.exists(path):
929
+ try:
930
+ with open(path, "r", encoding="utf-8") as f:
931
+ old_data = f.read()
932
+ if "report_failure" not in old_data:
933
+ os.remove(path)
934
+ except:
935
+ pass
936
+
937
+ if not os.path.exists(path):
938
+ with open(path, "w", encoding="utf-8") as f:
939
+ f.write(content.strip())
940
+
941
+ # اجرای تابع برای نوشتن کدهای پایتون به محض بارگذاری برنامه
942
+ write_default_scripts()
943
+
944
  def init_db():
945
  conn = sqlite3.connect(DB_PATH)
946
  c = conn.cursor()
 
1048
  except Exception:
1049
  return False
1050
 
1051
+ # تابع ارسال اکشن داینامیک به گیت‌هاب بر اساس کدهای پوشه yml
1052
  def dispatch_github_workflow(event_type, client_payload):
1053
  acc = get_next_account()
1054
  if not acc:
 
1063
  if os.path.exists(script_path):
1064
  with open(script_path, "r", encoding="utf-8") as f:
1065
  script_content = f.read()
1066
+
1067
  # تزریق داینامیک متغیرهایی که در یمل گیت‌هاب قرار ندارند برای تولید صدا
1068
  if event_type == "generate-audio":
1069
+ data_dict = client_payload.get("data", client_payload)
1070
+ audio_type = data_dict.get('audio_type', 'text')
1071
+ negative_prompt = data_dict.get('negative_prompt', 'music')
1072
+ script_content = script_content.replace("___AUDIO_TYPE___", str(audio_type))
1073
+ script_content = script_content.replace("___NEGATIVE_PROMPT___", str(negative_prompt))
1074
 
1075
  if "data" in client_payload:
1076
  client_payload["data"]["script"] = script_content
 
1204
  "deleted_count": deleted_count
1205
  })
1206
 
1207
+ # وب‌هوک مدیریت دریافت خطا و اجرای تلاش مجدد تا سقف ۵ بار بدون قید و شرط کلمه کلیدی
1208
  @app.route('/api/webhook/fail', methods=['POST'])
1209
  def webhook_fail():
1210
  data = request.get_json()
 
1308
  return jsonify({"status": "error", "message": "متن ورودی شامل کلمات نامناسب است."}), 400
1309
 
1310
  try:
1311
+ # تغییر مبدا به auto جهت پایداری بالا در پردازش حروف ترکیبی فارسی-انگلیسی
1312
  english_prompt = GoogleTranslator(source='auto', target='en').translate(persian_prompt)
1313
  print(f"[GOOGLE TRANSLATE SUCCESS] Original: {persian_prompt} -> Translated: {english_prompt}")
1314
  except Exception as e:
 
1586
  file_url = f"{SPACE_URL}/static/images/{filename}"
1587
 
1588
  client_payload = {
1589
+ "data": {
1590
+ "prompt": english_prompt,
1591
+ "negative_prompt": english_negative,
1592
+ "duration": duration,
1593
+ "seed": seed,
1594
+ "audio_type": audio_type,
1595
+ "file_url": file_url,
1596
+ "run_id": run_id,
1597
+ "space_url": SPACE_URL
1598
+ }
1599
  }
1600
 
1601
  r = dispatch_github_workflow("generate-audio", client_payload)
 
1644
 
1645
  # ==========================================
1646
 
1647
+
1648
  # --- API میکس ویدیوها سمت سرور ---
1649
  @app.route('/api/merge-videos', methods=['POST'])
1650
  def merge_videos():
 
1905
  conn.close()
1906
  return jsonify({"status": "success", "credits_remaining": credits_remaining})
1907
 
1908
+
1909
  # --- دریافت و ارسال فایل ---
1910
  @app.route('/api/webhook/upload', methods=['POST'])
1911
  def webhook_upload():