suprimedev commited on
Commit
1d71064
·
verified ·
1 Parent(s): cd88e5f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -55
app.py CHANGED
@@ -4,111 +4,127 @@ import requests
4
  import os
5
  import uuid
6
  import re # برای پردازش لینک‌های ورودی
 
7
 
8
  # پوشه برای ذخیره فایل‌های موقت
9
- TEMP_DIR = "temp_mp3_files"
10
  os.makedirs(TEMP_DIR, exist_ok=True) # اطمینان از وجود پوشه
11
 
12
- def download_mp3(url: str, output_path: str) -> bool:
13
  """
14
- فایل MP3 را از URL دانلود می‌کند.
15
- :param url: URL فایل MP3.
16
- :param output_path: مسیر کامل برای ذخیره فایل دانلود شده.
17
- :return: True اگر دانلود موفق بود، False در غیر این صورت.
18
  """
19
  try:
20
- response = requests.get(url, stream=True, timeout=30) # افزودن timeout
21
- response.raise_for_status() # برای HTTP errors (4xx or 5xx)
22
 
23
  content_type = response.headers.get('Content-Type', '')
24
- if 'audio' not in content_type and 'mpeg' not in content_type:
25
- print(f"URL: {url} does not seem to point to an audio file. Content-Type: {content_type}")
26
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  with open(output_path, 'wb') as f:
29
  for chunk in response.iter_content(chunk_size=8192):
30
- if chunk: # filter out keep-alive new chunks
31
  f.write(chunk)
32
- print(f"Successfully downloaded {url} to {output_path}")
33
- return True
34
  except requests.exceptions.Timeout:
35
  print(f"Error downloading {url}: Request timed out.")
36
- return False
37
  except requests.exceptions.RequestException as e:
38
  print(f"Error downloading {url}: {e}")
39
- return False
40
  except Exception as e:
41
  print(f"An unexpected error occurred during download of {url}: {e}")
42
- return False
43
 
44
- def merge_mp3_files_sequential(mp3_file_paths: list[str], output_file_path: str) -> bool:
45
  """
46
- چندین فایل MP3 را به ترتیب ادغام می‌کند.
47
- :param mp3_file_paths: لیستی از مسیرهای فایل‌های MP3.
48
- :param output_file_path: مسیر کامل برای ذخیره فایل ادغام شده.
49
  :return: True اگر ادغام موفق بود، False در غیر این صورت.
50
  """
51
- if not mp3_file_paths:
52
  return False
53
 
54
  combined_audio = None
55
  try:
56
  # بارگذاری اولین فایل
57
- combined_audio = AudioSegment.from_file(mp3_file_paths[0], format="mp3")
 
58
 
59
  # ادغام بقیه فایل‌ها
60
- for i, file_path in enumerate(mp3_file_paths[1:]):
61
- audio = AudioSegment.from_file(file_path, format="mp3")
62
  combined_audio += audio
63
  print(f"Merged {file_path}")
64
 
 
65
  combined_audio.export(output_file_path, format="mp3")
66
  print(f"Successfully merged all files to {output_file_path}")
67
  return True
68
  except Exception as e:
69
- print(f"Error merging MP3 files: {e}")
70
  return False
71
 
72
- def process_mp3_links_gradio(urls_input: str) -> tuple[str, str | None]:
73
  """
74
- این تابع اصلی Gradio است که رشته‌ای از لینک‌های MP3 را دریافت کرده،
75
  آن‌ها را دانلود و ادغام می‌کند.
76
- :param urls_input: رشته‌ای شامل لینک‌های MP3، جدا شده با خط جدید یا کاما.
77
  :return: یک تاپل شامل پیام وضعیت و مسیر فایل خروجی (یا None).
78
  """
79
  # پاکسازی و تقسیم لینک‌ها
80
- # حذف فضای خالی اطراف لینک‌ها و فیلتر کردن لینک‌های خالی
81
  urls = [url.strip() for url in re.split(r'[\n,]+', urls_input) if url.strip()]
82
 
83
  if not urls:
84
- return "لطفاً حداقل یک لینک MP3 معتبر وارد کنید.", None
85
 
86
  if len(urls) < 2:
87
- return "لطفاً حداقل دو لینک MP3 برای ادغام وارد کنید.", None
88
 
89
  status_message = ""
90
  output_audio_path = None
91
- downloaded_files_paths = [] # برای نگهداری مسیر فایل‌های دانلود شده
92
- temp_file_mapping = {} # URL -> temp_path
93
 
94
  # برای تولید یک unique_id برای هر درخواست
95
  request_unique_id = str(uuid.uuid4())
96
  request_temp_dir = os.path.join(TEMP_DIR, request_unique_id)
97
  os.makedirs(request_temp_dir, exist_ok=True)
98
 
99
-
100
  try:
101
  for i, url in enumerate(urls):
102
- temp_file_name = f"temp_{i+1}_{uuid.uuid4().hex}.mp3"
103
- temp_file_path = os.path.join(request_temp_dir, temp_file_name)
104
- temp_file_mapping[url] = temp_file_path # ذخیره mapping برای حذف آسان
105
-
106
  status_message += f"در حال دانلود فایل {i+1} از: {url}\n"
107
- gr.Info(f"Downloading file {i+1}...") # نمایش پیام در UI Gradio
108
- if download_mp3(url, temp_file_path):
109
- downloaded_files_paths.append(temp_file_path)
 
 
110
  else:
111
- return f"خطا در دانلود فایل {i+1} از '{url}'. لطفاً لینک را بررسی کنید.", None
112
 
113
  if not downloaded_files_paths:
114
  return "هیچ فایلی با موفقیت دانلود نشد. لطفاً لینک‌ها را بررسی کنید.", None
@@ -116,12 +132,12 @@ def process_mp3_links_gradio(urls_input: str) -> tuple[str, str | None]:
116
  status_message += "فایل‌ها با موفقیت دانلود شدند. در حال ادغام...\n"
117
  gr.Info("Files downloaded. Merging...")
118
 
119
- output_file_name = f"merged_output_{uuid.uuid4().hex}.mp3"
120
  output_file_path = os.path.join(request_temp_dir, output_file_name)
121
 
122
- if merge_mp3_files_sequential(downloaded_files_paths, output_file_path):
123
  status_message += f"فایل‌ها با موفقیت ادغام شدند. فایل خروجی: {output_file_path}\n"
124
- output_audio_path = output_file_path # مسیر فایل برای Gradio
125
  else:
126
  status_message += "خطا در ادغام فایل‌ها.\n"
127
 
@@ -129,7 +145,6 @@ def process_mp3_links_gradio(urls_input: str) -> tuple[str, str | None]:
129
  status_message += f"خطای پیش‌بینی نشده: {e}\n"
130
  finally:
131
  # حذف فایل‌های موقت پس از اتمام کار
132
- # حذف تنها فایل‌هایی که در این درخواست ایجاد شده‌اند
133
  for f in os.listdir(request_temp_dir):
134
  os.remove(os.path.join(request_temp_dir, f))
135
  os.rmdir(request_temp_dir)
@@ -141,20 +156,20 @@ def process_mp3_links_gradio(urls_input: str) -> tuple[str, str | None]:
141
  # برای بهبود تجربه کاربری در فضای هاگینگ‌فیس، بهتر است از theme=gr.themes.Soft() استفاده کنید.
142
  # و همچنین css را با دقت بیشتری برای RTL بنویسید.
143
  iface = gr.Interface(
144
- fn=process_mp3_links_gradio,
145
  inputs=[
146
  gr.Textbox(
147
- label="لینک‌های MP3 (هر لینک در یک خط یا با کاما جدا کنید)",
148
- placeholder="مثال:\nhttps://example.com/audio1.mp3\nhttps://example.com/audio2.mp3\n...",
149
- lines=5 # امکان ورود چند خط
150
  ),
151
  ],
152
  outputs=[
153
  gr.Textbox(label="وضعیت", interactive=False),
154
- gr.Audio(label="فایل MP3 ادغام شده", type="filepath")
155
  ],
156
- title="🎙️ ادغام کننده چندین فایل MP3 از طریق لینک 🎵",
157
- description="لینک‌های فایل‌های MP3 خود را (هر لینک در یک خط جدید یا با کاما جدا شده) در کادر زیر وارد کنید تا به صورت یک فایل ادغام شوند.",
158
  allow_flagging="never",
159
  theme=gr.themes.Soft(), # استفاده از تم جدید Gradio
160
  css="""
@@ -177,4 +192,4 @@ iface = gr.Interface(
177
 
178
  # تابع راه اندازی:
179
  if __name__ == "__main__":
180
- iface.launch(share=True) # share=True برای دسترسی عمومی در هاگینگ‌فیس یا از طریق لینک موقت
 
4
  import os
5
  import uuid
6
  import re # برای پردازش لینک‌های ورودی
7
+ import mimetypes # برای استفاده از Content-Type برای تخمین پسوند فایل
8
 
9
  # پوشه برای ذخیره فایل‌های موقت
10
+ TEMP_DIR = "temp_audio_files" # تغییر نام پوشه برای انعطاف‌پذیری بیشتر
11
  os.makedirs(TEMP_DIR, exist_ok=True) # اطمینان از وجود پوشه
12
 
13
+ def download_audio(url: str, output_dir: str) -> tuple[str | None, str | None]:
14
  """
15
+ فایل صوتی را از URL دانلود می‌کند.
16
+ :param url: URL فایل صوتی.
17
+ :param output_dir: دایرکتوری برای ذخیره فایل دانلود شده.
18
+ :return: (مسیر کامل فایل دانلود شده, پسوند تخمین زده شده) یا (None, None) در صورت خطا.
19
  """
20
  try:
21
+ response = requests.get(url, stream=True, timeout=30)
22
+ response.raise_for_status()
23
 
24
  content_type = response.headers.get('Content-Type', '')
25
+ # تخمین پسوند فایل بر اساس Content-Type
26
+ # اگر نتوانستیم پسوند را از Content-Type حدس بزنیم، از 'mp3' به عنوان پیش‌فرض استفاده می‌کنیم
27
+ # این کار ریسک ایجاد می‌کند ولی pydub ممکن است بتواند آن را تشخیص دهد.
28
+ estimated_extension = mimetypes.guess_extension(content_type)
29
+ if not estimated_extension:
30
+ # سعی می‌کنیم پسوند را از URL بگیریم
31
+ path_parts = os.path.splitext(url.split('?')[0])
32
+ if len(path_parts) > 1 and path_parts[1]:
33
+ estimated_extension = path_parts[1].lower()
34
+ else:
35
+ # اگر هیچ کدام جواب نداد، mp3 را به عنوان پیش‌فرض قرار می‌دهیم
36
+ # pydub در زمان بارگذاری فایل واقعی، فرمت را بررسی می‌کند.
37
+ estimated_extension = ".mp3"
38
+
39
+ # اطمینان از پاک بودن پسوند (بدون نقطه اضافی)
40
+ if estimated_extension.startswith('.'):
41
+ estimated_extension = estimated_extension[1:]
42
+
43
+ # ساخت نام فایل موقت
44
+ temp_file_name = f"temp_download_{uuid.uuid4().hex}.{estimated_extension}"
45
+ output_path = os.path.join(output_dir, temp_file_name)
46
 
47
  with open(output_path, 'wb') as f:
48
  for chunk in response.iter_content(chunk_size=8192):
49
+ if chunk:
50
  f.write(chunk)
51
+ print(f"Successfully downloaded {url} to {output_path} (estimated type: {estimated_extension})")
52
+ return output_path, estimated_extension
53
  except requests.exceptions.Timeout:
54
  print(f"Error downloading {url}: Request timed out.")
55
+ return None, None
56
  except requests.exceptions.RequestException as e:
57
  print(f"Error downloading {url}: {e}")
58
+ return None, None
59
  except Exception as e:
60
  print(f"An unexpected error occurred during download of {url}: {e}")
61
+ return None, None
62
 
63
+ def merge_audio_files_sequential(audio_file_paths: list[str], output_file_path: str) -> bool:
64
  """
65
+ چندین فایل صوتی را به ترتیب ادغام می‌کند. همه فایل‌ها به MP3 خروجی گرفته می‌شوند.
66
+ :param audio_file_paths: لیستی از مسیرهای فایل‌های صوتی (MP3, WAV, و غیره).
67
+ :param output_file_path: مسیر کامل برای ذخیره فایل ادغام شده (خروجی همیشه MP3 خواهد بود).
68
  :return: True اگر ادغام موفق بود، False در غیر این صورت.
69
  """
70
+ if not audio_file_paths:
71
  return False
72
 
73
  combined_audio = None
74
  try:
75
  # بارگذاری اولین فایل
76
+ # pydub به طور خودکار فرمت را از پسوند فایل تشخیص می‌دهد.
77
+ combined_audio = AudioSegment.from_file(audio_file_paths[0])
78
 
79
  # ادغام بقیه فایل‌ها
80
+ for i, file_path in enumerate(audio_file_paths[1:]):
81
+ audio = AudioSegment.from_file(file_path)
82
  combined_audio += audio
83
  print(f"Merged {file_path}")
84
 
85
+ # خروجی گرفتن نهایی به فرمت MP3
86
  combined_audio.export(output_file_path, format="mp3")
87
  print(f"Successfully merged all files to {output_file_path}")
88
  return True
89
  except Exception as e:
90
+ print(f"Error merging audio files: {e}")
91
  return False
92
 
93
+ def process_audio_links_gradio(urls_input: str) -> tuple[str, str | None]:
94
  """
95
+ این تابع اصلی Gradio است که رشته‌ای از لینک‌های صوتی را دریافت کرده،
96
  آن‌ها را دانلود و ادغام می‌کند.
97
+ :param urls_input: رشته‌ای شامل لینک‌های صوتی، جدا شده با خط جدید یا کاما.
98
  :return: یک تاپل شامل پیام وضعیت و مسیر فایل خروجی (یا None).
99
  """
100
  # پاکسازی و تقسیم لینک‌ها
 
101
  urls = [url.strip() for url in re.split(r'[\n,]+', urls_input) if url.strip()]
102
 
103
  if not urls:
104
+ return "لطفاً حداقل یک لینک فایل صوتی معتبر وارد کنید.", None
105
 
106
  if len(urls) < 2:
107
+ return "لطفاً حداقل دو لینک فایل صوتی برای ادغام وارد کنید.", None
108
 
109
  status_message = ""
110
  output_audio_path = None
111
+ downloaded_files_paths = []
 
112
 
113
  # برای تولید یک unique_id برای هر درخواست
114
  request_unique_id = str(uuid.uuid4())
115
  request_temp_dir = os.path.join(TEMP_DIR, request_unique_id)
116
  os.makedirs(request_temp_dir, exist_ok=True)
117
 
 
118
  try:
119
  for i, url in enumerate(urls):
 
 
 
 
120
  status_message += f"در حال دانلود فایل {i+1} از: {url}\n"
121
+ gr.Info(f"Downloading file {i+1}...")
122
+ # از تابع download_audio جدید استفاده می‌کنیم
123
+ downloaded_path, _ = download_audio(url, request_temp_dir)
124
+ if downloaded_path:
125
+ downloaded_files_paths.append(downloaded_path)
126
  else:
127
+ return f"خطا در دانلود فایل {i+1} از '{url}'. لطفاً لینک را بررسی کنید. ممکن است فایل صوتی نباشد.", None
128
 
129
  if not downloaded_files_paths:
130
  return "هیچ فایلی با موفقیت دانلود نشد. لطفاً لینک‌ها را بررسی کنید.", None
 
132
  status_message += "فایل‌ها با موفقیت دانلود شدند. در حال ادغام...\n"
133
  gr.Info("Files downloaded. Merging...")
134
 
135
+ output_file_name = f"merged_output_{uuid.uuid4().hex}.mp3" # خروجی همیشه MP3 خواهد بود
136
  output_file_path = os.path.join(request_temp_dir, output_file_name)
137
 
138
+ if merge_audio_files_sequential(downloaded_files_paths, output_file_path):
139
  status_message += f"فایل‌ها با موفقیت ادغام شدند. فایل خروجی: {output_file_path}\n"
140
+ output_audio_path = output_file_path
141
  else:
142
  status_message += "خطا در ادغام فایل‌ها.\n"
143
 
 
145
  status_message += f"خطای پیش‌بینی نشده: {e}\n"
146
  finally:
147
  # حذف فایل‌های موقت پس از اتمام کار
 
148
  for f in os.listdir(request_temp_dir):
149
  os.remove(os.path.join(request_temp_dir, f))
150
  os.rmdir(request_temp_dir)
 
156
  # برای بهبود تجربه کاربری در فضای هاگینگ‌فیس، بهتر است از theme=gr.themes.Soft() استفاده کنید.
157
  # و همچنین css را با دقت بیشتری برای RTL بنویسید.
158
  iface = gr.Interface(
159
+ fn=process_audio_links_gradio,
160
  inputs=[
161
  gr.Textbox(
162
+ label="لینک‌های فایل صوتی (MP3, WAV و غیره) - هر لینک در یک خط یا با کاما جدا کنید",
163
+ placeholder="مثال:\nhttps://example.com/audio1.mp3\nhttps://example.com/audio2.wav\n...",
164
+ lines=5
165
  ),
166
  ],
167
  outputs=[
168
  gr.Textbox(label="وضعیت", interactive=False),
169
+ gr.Audio(label="فایل صوتی ادغام شده", type="filepath")
170
  ],
171
+ title="🎙️ ادغام کننده چندین فایل صوتی از طریق لینک (MP3, WAV و غیره) 🎵",
172
+ description="لینک فایل‌های صوتی خود را (هر لینک در یک خط جدید یا با کاما جدا شده) در کادر زیر وارد کنید تا به صورت یک فایل **MP3** ادغام شوند.",
173
  allow_flagging="never",
174
  theme=gr.themes.Soft(), # استفاده از تم جدید Gradio
175
  css="""
 
192
 
193
  # تابع راه اندازی:
194
  if __name__ == "__main__":
195
+ iface.launch(share=True)