File size: 8,299 Bytes
aecade4 |
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 |
import os
import uuid
import requests
import threading
import time
from flask import Flask, request, jsonify, render_template, Response
from werkzeug.utils import secure_filename
from itertools import cycle
# --- 1. تنظیمات اصلی ---
WORKER_URLS = [
"https://ezmary-animat-worker1.hf.space",
"https://ezmary-animat-worker2.hf.space",
"https://ezmary-animat-worker3.hf.space",
"https://ezmary-animat-worker4.hf.space",
"https://ezmary-animat-worker5.hf.space",
]
worker_pool = cycle(WORKER_URLS)
UPLOADER_API_URL = "https://hamed744-uploadfile.hf.space/upload"
HF_TOKEN = os.getenv("HF_TOKEN")
app = Flask(__name__, template_folder='templates')
jobs = {}
lock = threading.Lock()
def get_next_worker_url():
return next(worker_pool)
# --- 2. توابع پسزمینه ---
def get_permanent_link(job_id, temp_render_url, worker_url):
try:
with lock: jobs[job_id]["status"] = "در حال دائمیسازی لینک..."
if not HF_TOKEN: raise Exception("توکن HF برای آپلود دائمی یافت نشد.")
video_full_url = f"{worker_url}{temp_render_url}"
payload = {'url': video_full_url}
headers = {'Authorization': f'Bearer {HF_TOKEN}'}
response = requests.post(UPLOADER_API_URL, json=payload, headers=headers, timeout=600)
response.raise_for_status()
data = response.json()
uploader_proxy_url = data.get("hf_url")
if not uploader_proxy_url: raise Exception("پاسخ از سرور آپلودر معتبر نبود.")
path_part = uploader_proxy_url.split('/proxy/')[1]
final_proxy_url = f"/proxy/{path_part}"
with lock:
jobs[job_id]["status"] = "completed"
jobs[job_id]["result"] = final_proxy_url
except Exception as e:
print(f"خطا در دائمیسازی لینک برای {job_id}: {e}")
with lock:
jobs[job_id]["status"] = "error"
jobs[job_id]["result"] = f"خطا در دائمیسازی لینک. لینک موقت: {temp_render_url}"
# <<< تابع اصلاح شده: حذف تایماوت و افزایش مقاومت در برابر خطای ارتباطی >>>
def poll_worker_service(job_id, render_job_id, worker_url):
POLLING_INTERVAL = 15
MAX_POLLING_ERRORS = 20 # اگر ۲۰ بار پشت سر هم (حدود ۵ دقیقه) نتوانستیم با کارگر صحبت کنیم، خطا میدهیم
error_count = 0
while True: # حلقه بینهایت، هرگز به خاطر زمان متوقف نمیشود
try:
response = requests.post(f"{worker_url}/check_job_status", json={"job_id": render_job_id}, timeout=45)
response.raise_for_status()
error_count = 0 # ریست کردن شمارنده خطا در صورت موفقیت
data = response.json()
with lock:
current_job = jobs.get(job_id)
if current_job and current_job.get("status") != data.get("status"):
current_job["status"] = data.get("status")
if data.get("status") == "completed":
get_permanent_link(job_id, data.get("result"), worker_url)
return # خروج از حلقه
elif data.get("status") == "error":
with lock:
current_job = jobs.get(job_id)
if current_job:
current_job["result"] = data.get("result", "خطای نامشخص از کارگر")
return # خروج از حلقه
except requests.exceptions.RequestException as e:
error_count += 1
print(f"خطای ارتباطی در پیگیری کارگر {worker_url} (تلاش {error_count}/{MAX_POLLING_ERRORS}): {e}")
if error_count >= MAX_POLLING_ERRORS:
with lock:
current_job = jobs.get(job_id)
if current_job:
current_job["status"] = "error"
current_job["result"] = f"ارتباط با سرور کارگر ({worker_url}) برای مدت طولانی قطع شد. ممکن است کارگر در حال ریاستارت باشد. لطفاً بعداً وضعیت را مجدداً بررسی کنید."
return # خروج از حلقه
time.sleep(POLLING_INTERVAL)
# --- 3. API Endpoints ---
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/submit_job', methods=['POST'])
def submit_job():
if 'image_file' not in request.files or 'video_file' not in request.files:
return jsonify({"error": "فایل عکس و ویدیو الزامی است."}), 400
image_file = request.files['image_file']
video_file = request.files['video_file']
motion = request.form.get('motion')
style = request.form.get('style')
image_bytes = image_file.read()
video_bytes = video_file.read()
MAX_RETRIES = 3
for attempt in range(MAX_RETRIES):
selected_worker_url = get_next_worker_url()
print(f"تلاش {attempt + 1}/{MAX_RETRIES}: ارسال کار به کارگر: {selected_worker_url}")
try:
response = requests.post(
f"{selected_worker_url}/submit_new_job",
files={'image_file': (secure_filename(image_file.filename), image_bytes, image_file.mimetype),
'video_file': (secure_filename(video_file.filename), video_bytes, video_file.mimetype)},
data={'motion': motion, 'style': style},
timeout=600
)
response.raise_for_status()
render_data = response.json()
render_job_id = render_data.get("job_id")
if not render_job_id: raise Exception("پاسخ معتبر از کارگر دریافت نشد.")
internal_job_id = str(uuid.uuid4())
with lock: jobs[internal_job_id] = {"status": "ارسال شده به کارگر...", "result": None}
thread = threading.Thread(target=poll_worker_service, args=(internal_job_id, render_job_id, selected_worker_url))
thread.start()
return jsonify({"job_id": internal_job_id})
except Exception as e:
print(f"خطا در تلاش {attempt + 1} برای کارگر {selected_worker_url}: {e}")
time.sleep(2)
final_error_message = ("<strong>سرورهای پردازش شلوغ هستند!</strong><br>"
"لطفاً چند دقیقه دیگر مجدداً تلاش نمایید.")
return jsonify({"error": final_error_message}), 500
@app.route('/api/check_status', methods=['POST'])
def check_status():
data = request.get_json()
job_id = data.get('job_id')
if not job_id: return jsonify({"error": "شناسه کار الزامی است."}), 400
with lock: job = jobs.get(job_id, {"status": "not_found", "result": None})
return jsonify({"status": job["status"], "result": job["result"]})
@app.route('/proxy/<user>/<repo>/<path:file_path>')
def file_proxy(user, repo, file_path):
if not HF_TOKEN: return "Token سرور برای پراکسی تنظیم نشده است.", 500
repo_id = f"{user}/{repo}"
file_url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_path}"
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
try:
hf_response = requests.get(file_url, headers=headers, stream=True, timeout=30)
hf_response.raise_for_status()
return Response(hf_response.iter_content(chunk_size=8192),
content_type=hf_response.headers.get('Content-Type', 'application/octet-stream'))
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404: return "فایل یافت نشد.", 404
return f"خطای سرور Hugging Face: {e.response.status_code}", e.response.status_code
except Exception as e:
print(f"خطای پراکسی: {e}")
return "خطا در واکشی فایل.", 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860))) |