suprimedev commited on
Commit
defad0a
·
verified ·
1 Parent(s): 9ddae4b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -229
app.py CHANGED
@@ -2,255 +2,127 @@ import gradio as gr
2
  from pydub import AudioSegment
3
  import requests
4
  import os
5
- import uuid
6
- import re
7
- import mimetypes
8
- import tempfile # برای مدیریت فایل‌های موقت
9
 
10
- # --- تنظیمات FFmpeg/ffprobe (برای محیط‌های محلی) ---
11
- # در محیط‌های Production مانند Hugging Face Spaces، معمولاً FFmpeg از قبل نصب شده است.
12
- # این خطوط تنها در صورتی نیاز است که با مشکل "FFmpeg or AVConvNotFound" مواجه شوید
13
- # و FFmpeg را در مسیرهای غیر استاندارد نصب کرده باشید.
14
- # PARENT_DIR = os.path.dirname(os.path.abspath(__file__))
15
- # FFMPEG_PATH = os.path.join(PARENT_DIR, "ffmpeg", "bin", "ffmpeg.exe") # مثال برای ویندوز
16
- # FFPROBE_PATH = os.path.join(PARENT_DIR, "ffmpeg", "bin", "ffprobe.exe") # مثال برای ویندوز
17
- #
18
- # if os.path.exists(FFMPEG_PATH):
19
- # AudioSegment.converter = FFMPEG_PATH
20
- # print(f"FFmpeg path set to: {FFMPEG_PATH}")
21
- # if os.path.exists(FFPROBE_PATH):
22
- # AudioSegment.ffprobe = FFPROBE_PATH
23
- # print(f"FFprobe path set to: {FFPROBE_PATH}")
24
- # else:
25
- # print("FFmpeg/FFprobe paths not explicitly set. Pydub will look in system PATH.")
26
 
27
- # یک تابع کمکی برای بررسی وجود FFmpeg
28
- def check_ffmpeg_presence():
29
  try:
30
- from pydub.utils import get_prober_name, get_encoder_name
31
- # تلاش برای فراخوانی ffprobe تا بررسی کنیم آیا در دسترس است یا نه
32
- output = os.popen(f"{get_prober_name()} -version").read()
33
- if "ffprobe" in output or "avprobe" in output:
34
- print("FFprobe is available.")
35
- return True
36
- else:
37
- print("FFprobe not found in system PATH or configured path.")
38
- return False
39
- except Exception as e:
40
- print(f"Error checking FFmpeg/FFprobe: {e}")
41
- return False
42
-
43
- # بررسی در ابتدای اجرا
44
- if not check_ffmpeg_presence():
45
- print("WARNING: FFmpeg/ffprobe might not be correctly installed or configured. Audio processing may fail.")
46
- # می‌توانید اینجا یک خطای Gradio به کاربر نشان دهید
47
- # gr.Warning("FFmpeg/ffprobe not found. Please ensure it's installed and accessible.")
48
-
49
-
50
- def download_audio(url: str, output_dir: str) -> str | None:
51
- """
52
- فایل صوتی را از URL دانلود می‌کند.
53
- :param url: URL فایل صوتی.
54
- :param output_dir: دایرکتوری برای ذخیره فایل دانلود شده.
55
- :return: مسیر کامل فایل دانلود شده یا None در صورت خطا.
56
- """
57
- try:
58
- response = requests.get(url, stream=True, timeout=30)
59
- response.raise_for_status()
60
-
61
- # استفاده از Content-Disposition برای نام فایل اگر موجود باشد
62
- content_disposition = response.headers.get('Content-Disposition')
63
- if content_disposition:
64
- fname_match = re.search(r'filename\*?=(?:UTF-8\'\')?\"?([^\"]+)\"?', content_disposition)
65
- if fname_match:
66
- original_filename = fname_match.group(1).encode('latin-1').decode('utf-8')
67
- # پاکسازی نام فایل برای جلوگیری از مشکلات مسیر
68
- original_filename = re.sub(r'[\\/:*?"<>|]', '_', original_filename)
69
- # اطمینان حاصل شود که پسوند دارد
70
- if not os.path.splitext(original_filename)[1]:
71
- original_filename += mimetypes.guess_extension(response.headers.get('Content-Type', ''), strict=False) or '.tmp'
72
- temp_filename = f"{uuid.uuid4().hex}_{original_filename}"
73
- else:
74
- temp_filename = f"temp_download_{uuid.uuid4().hex}.tmp"
75
- else:
76
- # تخمین پسوند از Content-Type یا URL
77
- content_type = response.headers.get('Content-Type', '')
78
- ext = mimetypes.guess_extension(content_type, strict=False)
79
- if not ext:
80
- path_parts = os.path.splitext(url.split('?')[0])
81
- if len(path_parts) > 1 and path_parts[1]:
82
- ext = path_parts[1].lower()
83
- else:
84
- ext = ".tmp" # پسوند موقت برای pydub تا خودش تشخیص دهد
85
-
86
- # ساخت نام فایل موقت (با پسوند مربوطه)
87
- temp_filename = f"temp_download_{uuid.uuid4().hex}{ext}"
88
-
89
- output_path = os.path.join(output_dir, temp_filename)
90
-
91
-
92
  with open(output_path, 'wb') as f:
93
  for chunk in response.iter_content(chunk_size=8192):
94
- if chunk:
95
- f.write(chunk)
96
- print(f"Successfully downloaded {url} to {output_path}")
97
- return output_path
98
- except requests.exceptions.Timeout:
99
- print(f"Error downloading {url}: Request timed out after 30 seconds.")
100
- return None
101
  except requests.exceptions.RequestException as e:
102
- print(f"Network or HTTP error downloading {url}: {e}")
103
- return None
104
  except Exception as e:
105
  print(f"An unexpected error occurred during download of {url}: {e}")
106
- return None
107
 
108
- def merge_audio_files_sequential(audio_file_paths: list[str], output_file_path: str) -> bool:
109
  """
110
- چندین فایل صوتی را به ترتیب ادغام می‌کند. همه فایل‌ها به MP3 خروجی گرفته می‌شوند.
111
- :param audio_file_paths: لیستی از مسیرهای فایل‌های صوتی.
112
- :param output_file_path: مسیر کامل برای ذخیره فایل ادغام شده (خروجی همیشه MP3 خواهد بود).
113
- :return: True اگر ادغام موفق بود، False در غیر این صورت.
114
  """
115
- if not audio_file_paths:
116
- print("No audio files provided for merging.")
117
- return False
118
-
119
- combined_audio = None
120
- successful_merges = 0
121
-
 
 
 
 
 
122
  try:
123
- # بارگذاری اولین فایل
124
- try:
125
- combined_audio = AudioSegment.from_file(audio_file_paths[0])
126
- print(f"Successfully loaded initial audio from: {audio_file_paths[0]}")
127
- successful_merges = 1
128
- except Exception as e:
129
- print(f"Error loading first audio file {audio_file_paths[0]}: {e}")
130
- return False
131
-
132
- # ادغام بقیه فایل‌ها
133
- for i, file_path in enumerate(audio_file_paths[1:]):
134
- try:
135
- audio = AudioSegment.from_file(file_path)
136
- combined_audio += audio
137
- print(f"Successfully merged {file_path}")
138
- successful_merges += 1
139
- except Exception as e:
140
- print(f"Error merging audio from {file_path}: {e}. Skipping this file.")
141
- # در اینجا می‌توانیم انتخاب کنیم که آیا کل عملیات را متوقف کنیم یا ادامه دهیم.
142
- # برای این کاربرد، ادامه دادن معقول‌تر است (ادغام بقیه فایل‌های موفق).
143
- pass # ادامه به فایل بعدی حتی اگر این یکی موفق نبود
144
-
145
- if successful_merges < len(audio_file_paths):
146
- print(f"Warning: Only {successful_merges} out of {len(audio_file_paths)} files were successfully loaded/merged.")
147
- if successful_merges == 0:
148
- print("No audio segments were successfully processed for merging.")
149
- return False
150
-
151
- # خروجی گرفتن نهایی به فرمت MP3
152
- combined_audio.export(output_file_path, format="mp3")
153
- print(f"Successfully exported final merged file to {output_file_path}")
154
- return True
155
  except Exception as e:
156
- print(f"An unexpected error occurred during audio merging process: {e}")
157
- return False
 
 
 
158
 
159
- def process_audio_links_gradio(urls_input: str) -> tuple[str, str | None]:
160
  """
161
- تابع اصلی Gradio برای پردازش لینک‌های صوتی.
 
162
  """
163
- urls = [url.strip() for url in re.split(r'[\n,]+', urls_input) if url.strip()]
164
-
165
- if not urls:
166
- return "لطفاً حداقل یک لینک فایل صوتی معتبر وارد کنید.", None
167
 
168
- if len(urls) < 2:
169
- return "برای ادغام، لطفاً حداقل دو لینک فایل صوتی وارد کنید.", None
 
170
 
171
- status_message = ""
172
- output_audio_path = None
173
- downloaded_files_paths = []
174
 
175
- # استفاده از NamedTemporaryFile برای دایرکتوری موقت
176
- # این دایرکتوری به طور خودکار پس از خروج از context manager حذف می‌شود.
177
- with tempfile.TemporaryDirectory() as request_temp_dir:
178
- print(f"Using temporary directory: {request_temp_dir}")
179
- try:
180
- for i, url in enumerate(urls):
181
- status_message += f"در حال دانلود فایل {i+1} از: {url}\n"
182
- gr.Info(f"Downloading file {i+1} of {len(urls)}...")
183
- downloaded_path = download_audio(url, request_temp_dir)
184
- if downloaded_path:
185
- downloaded_files_paths.append(downloaded_path)
186
- else:
187
- return f"خطا در دانلود فایل {i+1} از '{url}'. لطفاً لینک را بررسی کنید. ممکن است فایل صوتی نباشد یا دسترسی به آن ممکن نباشد.", None
188
-
189
- if not downloaded_files_paths:
190
- return "هیچ فایلی با موفقیت دانلود نشد. لطفاً لینک‌ها را بررسی کنید.", None
191
-
192
- # ترتیب دهی فایل ها بر اساس نام (برای تضمین ترتیب ادغام)
193
- downloaded_files_paths.sort()
194
-
195
- status_message += "فایل‌ها با موفقیت دانلود شدند. در حال ادغام...\n"
196
- gr.Info("Files downloaded. Merging audio segments...")
197
-
198
- output_file_name_final = f"merged_output_{uuid.uuid4().hex}.mp3"
199
- output_file_path_final = os.path.join(request_temp_dir, output_file_name_final)
200
-
201
- if merge_audio_files_sequential(downloaded_files_paths, output_file_path_final):
202
- status_message += f"فایل‌ها با موفقیت ادغام شدند. فایل خروجی: {output_file_path_final}\n"
203
- output_audio_path = output_file_path_final
204
- else:
205
- status_message += "خطا در ادغام فایل‌ها. (جزئیات خطا در کنسول سرور)\n"
206
-
207
- except Exception as e:
208
- status_message += f"خطای پیش‌بینی نشده در طول پردازش: {e}\n"
209
- print(f"Unhandled exception in process_audio_links_gradio: {e}")
210
- finally:
211
- # tempfile.TemporaryDirectory به صورت خودکار دایرکتوری را پاک می‌کند.
212
- # فقط باید مطمئن شویم که Gradio به فایل `output_audio_path` اشاره می‌کند
213
- # و gradigo خودش آن فایل را برای دانلود در اختیار قرار می‌دهد.
214
- print(f"Temporary directory {request_temp_dir} will be removed.")
215
 
216
- return status_message, output_audio_path
 
217
 
218
- # تعریف رابط Gradio
219
- # (بخش interface بدون تغییرات عمده)
220
- iface = gr.Interface(
221
- fn=process_audio_links_gradio,
222
- inputs=[
223
- gr.Textbox(
224
- label="لینک‌های فایل صوتی (MP3, WAV و غیره) - هر لینک در یک خط یا با کاما جدا کنید",
225
- placeholder="مثال:\nhttps://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3\nhttps://file-examples.com/storage/fe/2017/11/file_example_WAV_1MG.wav\n...",
226
- lines=5
227
- ),
228
- ],
229
- outputs=[
230
- gr.Textbox(label="وضعیت", interactive=False),
231
- gr.Audio(label="فایل صوتی ادغام شده", type="filepath")
232
- ],
233
- title="🎙️ ادغام کننده چندین فایل صوتی از طریق لینک (MP3, WAV و غیره) 🎵",
234
- description="لینک فایل‌های صوتی خود را (هر لینک در یک خط جدید یا با کاما جدا شده) در کادر زیر وارد کنید تا به صورت یک فایل **MP3** ادغام شوند.",
235
- allow_flagging="never",
236
- theme=gr.themes.Soft(),
237
- css="""
238
- body { font-family: 'Vazirmatn', sans-serif; direction: rtl; text-align: right; }
239
- h1, h2, h3, h4, h5, h6 { text-align: center; }
240
- .gr-textbox label, .gr-audio label { text-align: right; width: 100%; display: block; }
241
- .gradio-container {
242
- max-width: 800px;
243
- margin: auto;
244
- padding: 20px;
245
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
246
- border-radius: 8px;
247
- }
248
- .gr-button {
249
- background-color: #4CAF50;
250
- color: white;
251
- }
252
- """
253
- )
 
 
 
 
 
 
 
 
254
 
255
  if __name__ == "__main__":
256
- iface.launch(share=True)
 
 
2
  from pydub import AudioSegment
3
  import requests
4
  import os
5
+ import uuid # برای تولید نام فایل منحصر به فرد
 
 
 
6
 
7
+ # مسیر ذخیره فایل‌های موقت
8
+ TEMP_DIR = "temp_audio"
9
+ if not os.path.exists(TEMP_DIR):
10
+ os.makedirs(TEMP_DIR)
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ def download_file(url, output_path):
13
+ """فایل را از یک URL دانلود می‌کند."""
14
  try:
15
+ response = requests.get(url, stream=True)
16
+ response.raise_for_status() # خطایی رخ داد، آن را بالا ببر
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  with open(output_path, 'wb') as f:
18
  for chunk in response.iter_content(chunk_size=8192):
19
+ f.write(chunk)
20
+ return True
 
 
 
 
 
21
  except requests.exceptions.RequestException as e:
22
+ print(f"Error downloading {url}: {e}")
23
+ return False
24
  except Exception as e:
25
  print(f"An unexpected error occurred during download of {url}: {e}")
26
+ return False
27
 
28
+ def get_audio_from_input(input_source):
29
  """
30
+ منبع ورودی را پردازش کرده و یک شی AudioSegment برمی‌گرداند.
31
+ می‌تواند یک فایل محلی یا یک URL باشد.
 
 
32
  """
33
+ unique_filename = os.path.join(TEMP_DIR, str(uuid.uuid4()))
34
+
35
+ if input_source.startswith("http://") or input_source.startswith("https://"):
36
+ # این یک URL است، دانلودش کن
37
+ temp_filepath = unique_filename + "_downloaded" + os.path.splitext(input_source)[1].split("?")[0]
38
+ if not download_file(input_source, temp_filepath):
39
+ return None, f"خطا در دانلود فایل از لینک: {input_source}"
40
+ audio_path = temp_filepath
41
+ else:
42
+ # فرض می‌شود یک مسیر فایل محلی است
43
+ audio_path = input_source
44
+
45
  try:
46
+ audio = AudioSegment.from_file(audio_path)
47
+ return audio, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  except Exception as e:
49
+ return None, f"خطا در بارگذاری فایل صوتی ({audio_path}): {e}. مطمئن شوید فایل MP3 یا WAV معتبر است."
50
+ finally:
51
+ # اگر فایل از URL دانلود شده بود، آن را پاک کن
52
+ if 'temp_filepath' in locals() and os.path.exists(temp_filepath):
53
+ os.remove(temp_filepath)
54
 
55
+ def merge_audio_files(input_sources):
56
  """
57
+ چندین فایل صوتی را (از لینک یا فایل) ادغام می‌کند و یک فایل MP3 خروجی می‌دهد.
58
+ input_sources: یک رشته حاوی لینک‌ها یا مسیرهای فایل‌ها که با خط جدید جدا شده‌اند.
59
  """
60
+ if not input_sources.strip():
61
+ return None, "لطفاً حداقل یک لینک یا مسیر فایل وارد کنید."
 
 
62
 
63
+ sources = [s.strip() for s in input_sources.split('\n') if s.strip()]
64
+ if not sources:
65
+ return None, "خطا: لیست ورودی‌ها خالی است."
66
 
67
+ combined_audio = AudioSegment.empty()
68
+ errors = []
 
69
 
70
+ for source in sources:
71
+ audio_segment, error = get_audio_from_input(source)
72
+ if audio_segment:
73
+ combined_audio += audio_segment
74
+ else:
75
+ errors.append(error)
76
+ print(f"Skipping {source} due to error: {error}") # برای debugging کنسول
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ if not combined_audio.duration_seconds > 0:
79
+ return None, "هیچ فایل صوتی معتبری برای ادغام پیدا نشد. " + "\n".join(errors) if errors else ""
80
 
81
+ output_filename = os.path.join(TEMP_DIR, f"merged_audio_{uuid.uuid4()}.mp3")
82
+ try:
83
+ combined_audio.export(output_filename, format="mp3")
84
+ return output_filename, "عملیات موفقیت آمیز بود!"
85
+ except Exception as e:
86
+ return None, f"خطا در ذخیره فایل خروجی: {e}"
87
+
88
+ # ایجاد رابط کاربری Gradio
89
+ with gr.Blocks() as demo:
90
+ gr.Markdown(
91
+ """
92
+ # ابزار ادغام فایل‌های صوتی (MP3/WAV)
93
+ در اینجا می‌توانید لینک چندین فایل صوتی (MP3 یا WAV) را وارد کنید (هر لینک در یک خط جدید).
94
+ این ابزار تمام فایل‌ها را دانلود کرده و به یک فایل MP3 واحد ادغام می‌کند.
95
+ **نکته:** برای لینک‌های دانلود، مطمئن شوید قابلیت دانلود مستقیم دارند (نه صفحات وب).
96
+ """
97
+ )
98
+
99
+ audio_links_input = gr.Textbox(
100
+ label="لینک یا مسیر فایل‌های صوتی (هر کدام در یک خط جدید)",
101
+ placeholder="مثال:\nhttps://example.com/audio1.mp3\n./local_audio.wav\nhttps://example.com/audio2.wav",
102
+ lines=10
103
+ )
104
+
105
+ output_message = gr.Textbox(label="پیام", interactive=False)
106
+ output_audio = gr.Audio(label="فایل صوتی ادغام شده", type="filepath")
107
+
108
+ merge_button = gr.Button("ادغام فایل‌ها")
109
+
110
+ merge_button.click(
111
+ fn=merge_audio_files,
112
+ inputs=[audio_links_input],
113
+ outputs=[output_audio, output_message]
114
+ )
115
+
116
+ gr.Examples(
117
+ examples=[
118
+ ["https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3\nhttps://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3"],
119
+ # اگر فایل‌های واقعی داشته باشید، می‌توانید این خط را فعال کنید:
120
+ # ["./path/to/your/local_audio.mp3\n./path/to/another/local_audio.wav"]
121
+ ],
122
+ inputs=audio_links_input,
123
+ label="نمونه‌ها"
124
+ )
125
 
126
  if __name__ == "__main__":
127
+ demo.launch() # برای اجرا در لوکال
128
+ # demo.launch(share=True) # برای اشتراک‌گذاری موقت در یک لینک عمومی (برای هوش مصنوعی)