Ezmary commited on
Commit
985a6eb
·
verified ·
1 Parent(s): d15ee6a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -31
app.py CHANGED
@@ -33,11 +33,12 @@ os.makedirs("results", exist_ok=True)
33
 
34
  templates = Jinja2Templates(directory="templates")
35
 
36
- # --- الگوریتم برش (بدون تغییر) ---
37
  def find_split_points(audio_path, sr=24000):
38
  y, _ = librosa.load(audio_path, sr=sr)
39
  total_samples = len(y)
40
 
 
41
  MIN_PREFERRED = 8.0
42
  MAX_PREFERRED = 12.0
43
  MIN_HARD = 6.0
@@ -91,28 +92,46 @@ def find_split_points(audio_path, sr=24000):
91
 
92
  return split_points, y
93
 
94
- async def process_chunk_on_worker(worker_url, chunk_path, ref_path, index, job_id):
95
- async with aiohttp.ClientSession() as session:
 
 
 
 
 
96
  try:
97
- with open(chunk_path, 'rb') as f_chunk, open(ref_path, 'rb') as f_ref:
98
- data = aiohttp.FormData()
99
- data.add_field('source_file', f_chunk, filename='chunk.wav', content_type='audio/wav')
100
- data.add_field('ref_file', f_ref, filename='ref.wav', content_type='audio/wav')
101
-
102
- async with session.post(worker_url, data=data, timeout=300) as response:
103
- if response.status == 200:
104
- result_data = await response.read()
105
- out_path = f"temp/{job_id}_part_{index}.wav"
106
- with open(out_path, 'wb') as f_out:
107
- f_out.write(result_data)
108
- return index, out_path
109
- else:
110
- print(f"Worker {worker_url} failed with {response.status}")
111
- return index, None
 
 
 
 
 
 
112
  except Exception as e:
113
- print(f"Error calling worker {worker_url}: {e}")
114
- return index, None
 
 
 
 
 
 
115
 
 
116
  JOBS = {}
117
 
118
  async def job_manager_task(job_id, source_path, ref_path):
@@ -120,12 +139,12 @@ async def job_manager_task(job_id, source_path, ref_path):
120
  JOBS[job_id]["status"] = "processing"
121
  sr = 24000
122
 
123
- # تبدیل و تمیزکاری رفرنس
124
  ref_y, _ = librosa.load(ref_path, sr=sr)
125
  clean_ref_path = f"temp/{job_id}_ref_clean.wav"
126
  sf.write(clean_ref_path, ref_y, sr)
127
 
128
- # برش
129
  split_points, y = find_split_points(source_path, sr)
130
  total_chunks = len(split_points) - 1
131
  JOBS[job_id]["total_chunks"] = total_chunks
@@ -146,9 +165,10 @@ async def job_manager_task(job_id, source_path, ref_path):
146
  chunk_files.append((i, chunk_path, read_start, read_end, start, end))
147
 
148
  worker_url = WORKER_URLS[i % len(WORKER_URLS)]
149
- tasks.append(process_chunk_on_worker(worker_url, chunk_path, clean_ref_path, i, job_id))
 
150
 
151
- # اجرا
152
  results = []
153
  for f in asyncio.as_completed(tasks):
154
  idx, path = await f
@@ -156,9 +176,9 @@ async def job_manager_task(job_id, source_path, ref_path):
156
  JOBS[job_id]["completed_chunks"] += 1
157
  results.append((idx, path))
158
  else:
159
- print(f"Chunk {idx} failed completely.")
160
 
161
- # چسباندن
162
  results.sort(key=lambda x: x[0])
163
  processed_map = {idx: path for idx, path in results}
164
  final_audio = []
@@ -187,10 +207,12 @@ async def job_manager_task(job_id, source_path, ref_path):
187
  final_audio[-1][-fade_len:] = mixed
188
  valid_part = valid_part[fade_len:]
189
  final_audio.append(valid_part)
190
- except:
 
191
  real_len = split_points[i+1] - split_points[i]
192
  final_audio.append(np.zeros(real_len))
193
  else:
 
194
  real_len = split_points[i+1] - split_points[i]
195
  final_audio.append(np.zeros(real_len))
196
 
@@ -206,7 +228,7 @@ async def job_manager_task(job_id, source_path, ref_path):
206
  JOBS[job_id]["status"] = "completed"
207
  JOBS[job_id]["filename"] = out_filename
208
 
209
- # Cleanup
210
  if os.path.exists(clean_ref_path): os.remove(clean_ref_path)
211
  for _, c_path, _, _, _, _ in chunk_files:
212
  if os.path.exists(c_path): os.remove(c_path)
@@ -214,7 +236,7 @@ async def job_manager_task(job_id, source_path, ref_path):
214
  if os.path.exists(path): os.remove(path)
215
 
216
  except Exception as e:
217
- print(f"Job failed: {e}")
218
  JOBS[job_id]["status"] = "failed"
219
  finally:
220
  if os.path.exists(source_path): os.remove(source_path)
@@ -233,6 +255,7 @@ async def create_job(
233
  job_id = str(uuid.uuid4())
234
  source_path = f"temp/{job_id}_src.wav"
235
  ref_path = f"temp/{job_id}_ref.wav"
 
236
  with open(source_path, "wb") as b: shutil.copyfileobj(source_audio.file, b)
237
  with open(ref_path, "wb") as b: shutil.copyfileobj(ref_audio.file, b)
238
 
@@ -242,7 +265,10 @@ async def create_job(
242
  "completed_chunks": 0,
243
  "filename": None
244
  }
 
 
245
  background_tasks.add_task(job_manager_task, job_id, source_path, ref_path)
 
246
  return {"job_id": job_id}
247
 
248
  @app.get("/status/{job_id}")
@@ -255,10 +281,10 @@ def get_status(job_id: str):
255
  def download_file(filename: str):
256
  path = f"results/{filename}"
257
  if os.path.exists(path):
258
- # اضافه کردن هدر برای اطمینان از پخش صحیح در مرورگر
259
  return FileResponse(path, filename=filename, media_type="audio/wav")
260
  return {"error": "File not found"}
261
 
262
  if __name__ == "__main__":
263
  import uvicorn
264
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
33
 
34
  templates = Jinja2Templates(directory="templates")
35
 
36
+ # --- الگوریتم برش ---
37
  def find_split_points(audio_path, sr=24000):
38
  y, _ = librosa.load(audio_path, sr=sr)
39
  total_samples = len(y)
40
 
41
+ # تنظیمات برش برای پایداری بیشتر
42
  MIN_PREFERRED = 8.0
43
  MAX_PREFERRED = 12.0
44
  MIN_HARD = 6.0
 
92
 
93
  return split_points, y
94
 
95
+ async def process_chunk_with_retry(worker_url, chunk_path, ref_path, index, job_id, max_retries=3):
96
+ """ تابع ارسال به کارگر با قابلیت تلاش مجدد """
97
+
98
+ # افزایش تایم‌اوت به ۶۰۰ ثانیه (۱۰ دقیقه) برای هر تکه
99
+ timeout = aiohttp.ClientTimeout(total=600, connect=60, sock_read=600)
100
+
101
+ for attempt in range(max_retries):
102
  try:
103
+ async with aiohttp.ClientSession(timeout=timeout) as session:
104
+ with open(chunk_path, 'rb') as f_chunk, open(ref_path, 'rb') as f_ref:
105
+ data = aiohttp.FormData()
106
+ # هدرهای صحیح برای جلوگیری از رد شدن درخواست
107
+ data.add_field('source_file', f_chunk, filename='chunk.wav', content_type='audio/wav')
108
+ data.add_field('ref_file', f_ref, filename='ref.wav', content_type='audio/wav')
109
+
110
+ async with session.post(worker_url, data=data) as response:
111
+ if response.status == 200:
112
+ result_data = await response.read()
113
+ if len(result_data) < 1000: # چک کردن اینکه فایل خیلی کوچک (احتمالا ارور متنی) نباشد
114
+ print(f"Warning: Chunk {index} returned suspicious small data from {worker_url}")
115
+
116
+ out_path = f"temp/{job_id}_part_{index}.wav"
117
+ with open(out_path, 'wb') as f_out:
118
+ f_out.write(result_data)
119
+ return index, out_path
120
+ else:
121
+ # اگر سرور ارور داد (مثلا 500 یا 503)
122
+ print(f"Attempt {attempt+1}/{max_retries} failed for chunk {index} on {worker_url}: Status {response.status}")
123
+
124
  except Exception as e:
125
+ print(f"Attempt {attempt+1}/{max_retries} connection error for chunk {index} on {worker_url}: {e}")
126
+
127
+ # اگر خطا خورد، کمی صبر کن و دوباره تلاش کن (Backoff)
128
+ if attempt < max_retries - 1:
129
+ await asyncio.sleep(2 * (attempt + 1))
130
+
131
+ print(f"All {max_retries} attempts failed for chunk {index}.")
132
+ return index, None
133
 
134
+ # حافظه موقت جاب‌ها
135
  JOBS = {}
136
 
137
  async def job_manager_task(job_id, source_path, ref_path):
 
139
  JOBS[job_id]["status"] = "processing"
140
  sr = 24000
141
 
142
+ # آماده‌سازی رفرنس
143
  ref_y, _ = librosa.load(ref_path, sr=sr)
144
  clean_ref_path = f"temp/{job_id}_ref_clean.wav"
145
  sf.write(clean_ref_path, ref_y, sr)
146
 
147
+ # برش فایل اصلی
148
  split_points, y = find_split_points(source_path, sr)
149
  total_chunks = len(split_points) - 1
150
  JOBS[job_id]["total_chunks"] = total_chunks
 
165
  chunk_files.append((i, chunk_path, read_start, read_end, start, end))
166
 
167
  worker_url = WORKER_URLS[i % len(WORKER_URLS)]
168
+ # استفاده از تابع جدید با قابلیت Retry
169
+ tasks.append(process_chunk_with_retry(worker_url, chunk_path, clean_ref_path, i, job_id))
170
 
171
+ # اجرای تسک‌ها
172
  results = []
173
  for f in asyncio.as_completed(tasks):
174
  idx, path = await f
 
176
  JOBS[job_id]["completed_chunks"] += 1
177
  results.append((idx, path))
178
  else:
179
+ print(f"Chunk {idx} failed completely after retries.")
180
 
181
+ # چسباندن (Stitching)
182
  results.sort(key=lambda x: x[0])
183
  processed_map = {idx: path for idx, path in results}
184
  final_audio = []
 
207
  final_audio[-1][-fade_len:] = mixed
208
  valid_part = valid_part[fade_len:]
209
  final_audio.append(valid_part)
210
+ except Exception as e:
211
+ print(f"Stitch error {i}: {e}")
212
  real_len = split_points[i+1] - split_points[i]
213
  final_audio.append(np.zeros(real_len))
214
  else:
215
+ # اگر تکه‌ای کلاً خراب شد، سکوت می‌گذاریم که تایم کلی به هم نریزد
216
  real_len = split_points[i+1] - split_points[i]
217
  final_audio.append(np.zeros(real_len))
218
 
 
228
  JOBS[job_id]["status"] = "completed"
229
  JOBS[job_id]["filename"] = out_filename
230
 
231
+ # پاکسازی
232
  if os.path.exists(clean_ref_path): os.remove(clean_ref_path)
233
  for _, c_path, _, _, _, _ in chunk_files:
234
  if os.path.exists(c_path): os.remove(c_path)
 
236
  if os.path.exists(path): os.remove(path)
237
 
238
  except Exception as e:
239
+ print(f"Job failed fatal: {e}")
240
  JOBS[job_id]["status"] = "failed"
241
  finally:
242
  if os.path.exists(source_path): os.remove(source_path)
 
255
  job_id = str(uuid.uuid4())
256
  source_path = f"temp/{job_id}_src.wav"
257
  ref_path = f"temp/{job_id}_ref.wav"
258
+
259
  with open(source_path, "wb") as b: shutil.copyfileobj(source_audio.file, b)
260
  with open(ref_path, "wb") as b: shutil.copyfileobj(ref_audio.file, b)
261
 
 
265
  "completed_chunks": 0,
266
  "filename": None
267
  }
268
+
269
+ # اجرای تسک در پس‌زمینه (حتی اگر کاربر قطع شود ادامه می‌یابد)
270
  background_tasks.add_task(job_manager_task, job_id, source_path, ref_path)
271
+
272
  return {"job_id": job_id}
273
 
274
  @app.get("/status/{job_id}")
 
281
  def download_file(filename: str):
282
  path = f"results/{filename}"
283
  if os.path.exists(path):
 
284
  return FileResponse(path, filename=filename, media_type="audio/wav")
285
  return {"error": "File not found"}
286
 
287
  if __name__ == "__main__":
288
  import uvicorn
289
+ # افزایش تایم‌اوت خود uvicorn برای اطمینان
290
+ uvicorn.run(app, host="0.0.0.0", port=7860, timeout_keep_alive=300)