Opera8 commited on
Commit
397572e
·
verified ·
1 Parent(s): 8d55a91

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -42
app.py CHANGED
@@ -8,25 +8,13 @@ from werkzeug.utils import secure_filename
8
  from itertools import cycle
9
  from dotenv import load_dotenv
10
 
11
- # بارگذاری متغیرهای محیطی از فایل .env (برای تست لوکال)
12
  load_dotenv()
13
 
14
- # --- 1. تنظیمات اصلی ---
15
-
16
- # دریافت لیست کارگرها از متغیر محیطی
17
- # آدرس‌ها باید با کاما (,) جدا شده باشند
18
  workers_env = os.getenv("WORKER_NODES", "")
19
-
20
  if not workers_env:
21
- # اگر متغیر تنظیم نشده باشد، برنامه با خطا متوقف می‌شود تا متوجه شوید
22
- raise ValueError("خطا: متغیر محیطی WORKER_NODES تنظیم نشده است. لطفاً آدرس کارگرها را در تنظیمات اضافه کنید.")
23
 
24
- # تبدیل رشته به لیست و حذف فاصله‌های اضافی
25
  WORKER_URLS = [url.strip() for url in workers_env.split(',') if url.strip()]
26
-
27
- if not WORKER_URLS:
28
- raise ValueError("خطا: لیست کارگرها خالی است.")
29
-
30
  worker_pool = cycle(WORKER_URLS)
31
 
32
  UPLOADER_API_URL = "https://hamed744-uploadfile.hf.space/upload"
@@ -40,11 +28,10 @@ lock = threading.Lock()
40
  def get_next_worker_url():
41
  return next(worker_pool)
42
 
43
- # --- 2. توابع پس‌زمینه ---
44
  def get_permanent_link(job_id, temp_render_url, worker_url):
45
  try:
46
  with lock: jobs[job_id]["status"] = "در حال دائمی‌سازی لینک..."
47
- if not HF_TOKEN: raise Exception("توکن HF برای آپلود دائمی یافت نشد.")
48
 
49
  video_full_url = f"{worker_url}{temp_render_url}"
50
 
@@ -55,7 +42,7 @@ def get_permanent_link(job_id, temp_render_url, worker_url):
55
  data = response.json()
56
 
57
  uploader_proxy_url = data.get("hf_url")
58
- if not uploader_proxy_url: raise Exception("پاسخ از سرور آپلودر معتبر نبود.")
59
 
60
  path_part = uploader_proxy_url.split('/proxy/')[1]
61
  final_proxy_url = f"/proxy/{path_part}"
@@ -65,22 +52,21 @@ def get_permanent_link(job_id, temp_render_url, worker_url):
65
  jobs[job_id]["result"] = final_proxy_url
66
 
67
  except Exception as e:
68
- print(f"خطا در دائمی‌سازی لینک برای {job_id}: {e}")
69
  with lock:
70
  jobs[job_id]["status"] = "error"
71
- jobs[job_id]["result"] = f"خطا در دائمی‌سازی لینک. لینک موقت: {temp_render_url}"
72
 
73
- # <<< تابع اصلاح شده: حذف تایم‌اوت و افزایش مقاومت در برابر خطای ارتباطی >>>
74
  def poll_worker_service(job_id, render_job_id, worker_url):
75
  POLLING_INTERVAL = 15
76
- MAX_POLLING_ERRORS = 20 # اگر ۲۰ بار پشت سر هم (حدود ۵ دقیقه) نتوانستیم با کارگر صحبت کنیم، خطا می‌دهیم
77
  error_count = 0
78
 
79
- while True: # حلقه بی‌نهایت، هرگز به خاطر زمان متوقف نمی‌شود
80
  try:
81
  response = requests.post(f"{worker_url}/check_job_status", json={"job_id": render_job_id}, timeout=45)
82
  response.raise_for_status()
83
- error_count = 0 # ریست کردن شمارنده خطا در صورت موفقیت
84
 
85
  data = response.json()
86
  with lock:
@@ -90,27 +76,26 @@ def poll_worker_service(job_id, render_job_id, worker_url):
90
 
91
  if data.get("status") == "completed":
92
  get_permanent_link(job_id, data.get("result"), worker_url)
93
- return # خروج از حلقه
94
  elif data.get("status") == "error":
95
  with lock:
96
  current_job = jobs.get(job_id)
97
  if current_job:
98
- current_job["result"] = data.get("result", "خطای نامشخص از کارگر")
99
- return # خروج از حلقه
100
  except requests.exceptions.RequestException as e:
101
  error_count += 1
102
- print(f"خطای ارتباطی در پیگیری کارگر {worker_url} (تلاش {error_count}/{MAX_POLLING_ERRORS}): {e}")
103
  if error_count >= MAX_POLLING_ERRORS:
104
  with lock:
105
  current_job = jobs.get(job_id)
106
  if current_job:
107
  current_job["status"] = "error"
108
- current_job["result"] = f"ارتباط با سرور کارگر ({worker_url}) برای مدت طولانی قطع شد. ممکن است کارگر در حال ری‌استارت باشد. لطفاً بعداً وضعیت را مجدداً بررسی کنید."
109
- return # خروج از حلقه
110
 
111
  time.sleep(POLLING_INTERVAL)
112
 
113
- # --- 3. API Endpoints ---
114
  @app.route('/')
115
  def index():
116
  return render_template('index.html')
@@ -118,7 +103,7 @@ def index():
118
  @app.route('/api/submit_job', methods=['POST'])
119
  def submit_job():
120
  if 'image_file' not in request.files or 'video_file' not in request.files:
121
- return jsonify({"error": "فایل عکس و ویدیو الزامی است."}), 400
122
 
123
  image_file = request.files['image_file']
124
  video_file = request.files['video_file']
@@ -131,7 +116,7 @@ def submit_job():
131
  MAX_RETRIES = 3
132
  for attempt in range(MAX_RETRIES):
133
  selected_worker_url = get_next_worker_url()
134
- print(f"تلاش {attempt + 1}/{MAX_RETRIES}: ارسال کار به کارگر: {selected_worker_url}")
135
  try:
136
  response = requests.post(
137
  f"{selected_worker_url}/submit_new_job",
@@ -143,33 +128,33 @@ def submit_job():
143
  response.raise_for_status()
144
  render_data = response.json()
145
  render_job_id = render_data.get("job_id")
146
- if not render_job_id: raise Exception("پاسخ معتبر از کارگر دریافت نشد.")
147
 
148
  internal_job_id = str(uuid.uuid4())
149
- with lock: jobs[internal_job_id] = {"status": "ارسال شده به کارگر...", "result": None}
150
 
151
  thread = threading.Thread(target=poll_worker_service, args=(internal_job_id, render_job_id, selected_worker_url))
152
  thread.start()
153
  return jsonify({"job_id": internal_job_id})
154
  except Exception as e:
155
- print(f"خطا در تلاش {attempt + 1} برای کارگر {selected_worker_url}: {e}")
156
  time.sleep(2)
157
 
158
- final_error_message = ("<strong>سرورهای پردازش شلوغ هستند!</strong><br>"
159
- "لطفاً چند دقیقه دیگر مجدداً تلاش نمایید.")
160
  return jsonify({"error": final_error_message}), 500
161
 
162
  @app.route('/api/check_status', methods=['POST'])
163
  def check_status():
164
  data = request.get_json()
165
  job_id = data.get('job_id')
166
- if not job_id: return jsonify({"error": "شناسه کار الزامی است."}), 400
167
  with lock: job = jobs.get(job_id, {"status": "not_found", "result": None})
168
  return jsonify({"status": job["status"], "result": job["result"]})
169
 
170
  @app.route('/proxy/<user>/<repo>/<path:file_path>')
171
  def file_proxy(user, repo, file_path):
172
- if not HF_TOKEN: return "Token سرور برای پراکسی تنظیم نشده است.", 500
173
  repo_id = f"{user}/{repo}"
174
  file_url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_path}"
175
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
@@ -179,11 +164,11 @@ def file_proxy(user, repo, file_path):
179
  return Response(hf_response.iter_content(chunk_size=8192),
180
  content_type=hf_response.headers.get('Content-Type', 'application/octet-stream'))
181
  except requests.exceptions.HTTPError as e:
182
- if e.response.status_code == 404: return "فایل یافت نشد.", 404
183
- return f"خطای سرور Hugging Face: {e.response.status_code}", e.response.status_code
184
  except Exception as e:
185
- print(f"خطای پراکسی: {e}")
186
- return "خطا در واکشی فایل.", 500
187
 
188
  if __name__ == '__main__':
189
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
 
8
  from itertools import cycle
9
  from dotenv import load_dotenv
10
 
 
11
  load_dotenv()
12
 
 
 
 
 
13
  workers_env = os.getenv("WORKER_NODES", "")
 
14
  if not workers_env:
15
+ raise ValueError("WORKER_NODES environment variable is not set")
 
16
 
 
17
  WORKER_URLS = [url.strip() for url in workers_env.split(',') if url.strip()]
 
 
 
 
18
  worker_pool = cycle(WORKER_URLS)
19
 
20
  UPLOADER_API_URL = "https://hamed744-uploadfile.hf.space/upload"
 
28
  def get_next_worker_url():
29
  return next(worker_pool)
30
 
 
31
  def get_permanent_link(job_id, temp_render_url, worker_url):
32
  try:
33
  with lock: jobs[job_id]["status"] = "در حال دائمی‌سازی لینک..."
34
+ if not HF_TOKEN: raise Exception("HF_TOKEN not found")
35
 
36
  video_full_url = f"{worker_url}{temp_render_url}"
37
 
 
42
  data = response.json()
43
 
44
  uploader_proxy_url = data.get("hf_url")
45
+ if not uploader_proxy_url: raise Exception("Invalid response from uploader")
46
 
47
  path_part = uploader_proxy_url.split('/proxy/')[1]
48
  final_proxy_url = f"/proxy/{path_part}"
 
52
  jobs[job_id]["result"] = final_proxy_url
53
 
54
  except Exception as e:
55
+ print(f"Error making link permanent for {job_id}: {e}")
56
  with lock:
57
  jobs[job_id]["status"] = "error"
58
+ jobs[job_id]["result"] = f"Error making link permanent. Temp link: {temp_render_url}"
59
 
 
60
  def poll_worker_service(job_id, render_job_id, worker_url):
61
  POLLING_INTERVAL = 15
62
+ MAX_POLLING_ERRORS = 20
63
  error_count = 0
64
 
65
+ while True:
66
  try:
67
  response = requests.post(f"{worker_url}/check_job_status", json={"job_id": render_job_id}, timeout=45)
68
  response.raise_for_status()
69
+ error_count = 0
70
 
71
  data = response.json()
72
  with lock:
 
76
 
77
  if data.get("status") == "completed":
78
  get_permanent_link(job_id, data.get("result"), worker_url)
79
+ return
80
  elif data.get("status") == "error":
81
  with lock:
82
  current_job = jobs.get(job_id)
83
  if current_job:
84
+ current_job["result"] = data.get("result", "Unknown worker error")
85
+ return
86
  except requests.exceptions.RequestException as e:
87
  error_count += 1
88
+ print(f"Connection error polling worker {worker_url} ({error_count}/{MAX_POLLING_ERRORS}): {e}")
89
  if error_count >= MAX_POLLING_ERRORS:
90
  with lock:
91
  current_job = jobs.get(job_id)
92
  if current_job:
93
  current_job["status"] = "error"
94
+ current_job["result"] = f"Connection to worker ({worker_url}) lost."
95
+ return
96
 
97
  time.sleep(POLLING_INTERVAL)
98
 
 
99
  @app.route('/')
100
  def index():
101
  return render_template('index.html')
 
103
  @app.route('/api/submit_job', methods=['POST'])
104
  def submit_job():
105
  if 'image_file' not in request.files or 'video_file' not in request.files:
106
+ return jsonify({"error": "Image and video files are required"}), 400
107
 
108
  image_file = request.files['image_file']
109
  video_file = request.files['video_file']
 
116
  MAX_RETRIES = 3
117
  for attempt in range(MAX_RETRIES):
118
  selected_worker_url = get_next_worker_url()
119
+ print(f"Attempt {attempt + 1}/{MAX_RETRIES}: Sending to worker: {selected_worker_url}")
120
  try:
121
  response = requests.post(
122
  f"{selected_worker_url}/submit_new_job",
 
128
  response.raise_for_status()
129
  render_data = response.json()
130
  render_job_id = render_data.get("job_id")
131
+ if not render_job_id: raise Exception("Invalid response from worker")
132
 
133
  internal_job_id = str(uuid.uuid4())
134
+ with lock: jobs[internal_job_id] = {"status": "Sending to worker...", "result": None}
135
 
136
  thread = threading.Thread(target=poll_worker_service, args=(internal_job_id, render_job_id, selected_worker_url))
137
  thread.start()
138
  return jsonify({"job_id": internal_job_id})
139
  except Exception as e:
140
+ print(f"Error on attempt {attempt + 1} for worker {selected_worker_url}: {e}")
141
  time.sleep(2)
142
 
143
+ final_error_message = ("<strong>Servers are busy!</strong><br>"
144
+ "Please try again in a few minutes.")
145
  return jsonify({"error": final_error_message}), 500
146
 
147
  @app.route('/api/check_status', methods=['POST'])
148
  def check_status():
149
  data = request.get_json()
150
  job_id = data.get('job_id')
151
+ if not job_id: return jsonify({"error": "Job ID is required"}), 400
152
  with lock: job = jobs.get(job_id, {"status": "not_found", "result": None})
153
  return jsonify({"status": job["status"], "result": job["result"]})
154
 
155
  @app.route('/proxy/<user>/<repo>/<path:file_path>')
156
  def file_proxy(user, repo, file_path):
157
+ if not HF_TOKEN: return "HF_TOKEN not set", 500
158
  repo_id = f"{user}/{repo}"
159
  file_url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_path}"
160
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
 
164
  return Response(hf_response.iter_content(chunk_size=8192),
165
  content_type=hf_response.headers.get('Content-Type', 'application/octet-stream'))
166
  except requests.exceptions.HTTPError as e:
167
+ if e.response.status_code == 404: return "File not found", 404
168
+ return f"Hugging Face Server Error: {e.response.status_code}", e.response.status_code
169
  except Exception as e:
170
+ print(f"Proxy error: {e}")
171
+ return "Error fetching file", 500
172
 
173
  if __name__ == '__main__':
174
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))