import gradio as gr from pydub import AudioSegment import requests import os import uuid import re # برای پردازش لینک‌های ورودی import mimetypes # برای استفاده از Content-Type برای تخمین پسوند فایل # پوشه برای ذخیره فایل‌های موقت TEMP_DIR = "temp_audio_files" # تغییر نام پوشه برای انعطاف‌پذیری بیشتر os.makedirs(TEMP_DIR, exist_ok=True) # اطمینان از وجود پوشه def download_audio(url: str, output_dir: str) -> tuple[str | None, str | None]: """ فایل صوتی را از URL دانلود می‌کند. :param url: URL فایل صوتی. :param output_dir: دایرکتوری برای ذخیره فایل دانلود شده. :return: (مسیر کامل فایل دانلود شده, پسوند تخمین زده شده) یا (None, None) در صورت خطا. """ try: response = requests.get(url, stream=True, timeout=30) response.raise_for_status() content_type = response.headers.get('Content-Type', '') # تخمین پسوند فایل بر اساس Content-Type # اگر نتوانستیم پسوند را از Content-Type حدس بزنیم، از 'mp3' به عنوان پیش‌فرض استفاده می‌کنیم # این کار ریسک ایجاد می‌کند ولی pydub ممکن است بتواند آن را تشخیص دهد. estimated_extension = mimetypes.guess_extension(content_type) if not estimated_extension: # سعی می‌کنیم پسوند را از URL بگیریم path_parts = os.path.splitext(url.split('?')[0]) if len(path_parts) > 1 and path_parts[1]: estimated_extension = path_parts[1].lower() else: # اگر هیچ کدام جواب نداد، mp3 را به عنوان پیش‌فرض قرار می‌دهیم # pydub در زمان بارگذاری فایل واقعی، فرمت را بررسی می‌کند. estimated_extension = ".mp3" # اطمینان از پاک بودن پسوند (بدون نقطه اضافی) if estimated_extension.startswith('.'): estimated_extension = estimated_extension[1:] # ساخت نام فایل موقت temp_file_name = f"temp_download_{uuid.uuid4().hex}.{estimated_extension}" output_path = os.path.join(output_dir, temp_file_name) with open(output_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: f.write(chunk) print(f"Successfully downloaded {url} to {output_path} (estimated type: {estimated_extension})") return output_path, estimated_extension except requests.exceptions.Timeout: print(f"Error downloading {url}: Request timed out.") return None, None except requests.exceptions.RequestException as e: print(f"Error downloading {url}: {e}") return None, None except Exception as e: print(f"An unexpected error occurred during download of {url}: {e}") return None, None def merge_audio_files_sequential(audio_file_paths: list[str], output_file_path: str) -> bool: """ چندین فایل صوتی را به ترتیب ادغام می‌کند. همه فایل‌ها به MP3 خروجی گرفته می‌شوند. :param audio_file_paths: لیستی از مسیرهای فایل‌های صوتی (MP3, WAV, و غیره). :param output_file_path: مسیر کامل برای ذخیره فایل ادغام شده (خروجی همیشه MP3 خواهد بود). :return: True اگر ادغام موفق بود، False در غیر این صورت. """ if not audio_file_paths: return False combined_audio = None try: # بارگذاری اولین فایل # pydub به طور خودکار فرمت را از پسوند فایل تشخیص می‌دهد. combined_audio = AudioSegment.from_file(audio_file_paths[0]) # ادغام بقیه فایل‌ها for i, file_path in enumerate(audio_file_paths[1:]): audio = AudioSegment.from_file(file_path) combined_audio += audio print(f"Merged {file_path}") # خروجی گرفتن نهایی به فرمت MP3 combined_audio.export(output_file_path, format="mp3") print(f"Successfully merged all files to {output_file_path}") return True except Exception as e: print(f"Error merging audio files: {e}") return False def process_audio_links_gradio(urls_input: str) -> tuple[str, str | None]: """ این تابع اصلی Gradio است که رشته‌ای از لینک‌های صوتی را دریافت کرده، آن‌ها را دانلود و ادغام می‌کند. :param urls_input: رشته‌ای شامل لینک‌های صوتی، جدا شده با خط جدید یا کاما. :return: یک تاپل شامل پیام وضعیت و مسیر فایل خروجی (یا None). """ # پاکسازی و تقسیم لینک‌ها urls = [url.strip() for url in re.split(r'[\n,]+', urls_input) if url.strip()] if not urls: return "لطفاً حداقل یک لینک فایل صوتی معتبر وارد کنید.", None if len(urls) < 2: return "لطفاً حداقل دو لینک فایل صوتی برای ادغام وارد کنید.", None status_message = "" output_audio_path = None downloaded_files_paths = [] # برای تولید یک unique_id برای هر درخواست request_unique_id = str(uuid.uuid4()) request_temp_dir = os.path.join(TEMP_DIR, request_unique_id) os.makedirs(request_temp_dir, exist_ok=True) try: for i, url in enumerate(urls): status_message += f"در حال دانلود فایل {i+1} از: {url}\n" gr.Info(f"Downloading file {i+1}...") # از تابع download_audio جدید استفاده می‌کنیم downloaded_path, _ = download_audio(url, request_temp_dir) if downloaded_path: downloaded_files_paths.append(downloaded_path) else: return f"خطا در دانلود فایل {i+1} از '{url}'. لطفاً لینک را بررسی کنید. ممکن است فایل صوتی نباشد.", None if not downloaded_files_paths: return "هیچ فایلی با موفقیت دانلود نشد. لطفاً لینک‌ها را بررسی کنید.", None status_message += "فایل‌ها با موفقیت دانلود شدند. در حال ادغام...\n" gr.Info("Files downloaded. Merging...") output_file_name = f"merged_output_{uuid.uuid4().hex}.mp3" # خروجی همیشه MP3 خواهد بود output_file_path = os.path.join(request_temp_dir, output_file_name) if merge_audio_files_sequential(downloaded_files_paths, output_file_path): status_message += f"فایل‌ها با موفقیت ادغام شدند. فایل خروجی: {output_file_path}\n" output_audio_path = output_file_path else: status_message += "خطا در ادغام فایل‌ها.\n" except Exception as e: status_message += f"خطای پیش‌بینی نشده: {e}\n" finally: # حذف فایل‌های موقت پس از اتمام کار for f in os.listdir(request_temp_dir): os.remove(os.path.join(request_temp_dir, f)) os.rmdir(request_temp_dir) print(f"Removed temporary directory: {request_temp_dir}") return status_message, output_audio_path # تعریف رابط Gradio # برای بهبود تجربه کاربری در فضای هاگینگ‌فیس، بهتر است از theme=gr.themes.Soft() استفاده کنید. # و همچنین css را با دقت بیشتری برای RTL بنویسید. iface = gr.Interface( fn=process_audio_links_gradio, inputs=[ gr.Textbox( label="لینک‌های فایل صوتی (MP3, WAV و غیره) - هر لینک در یک خط یا با کاما جدا کنید", placeholder="مثال:\nhttps://example.com/audio1.mp3\nhttps://example.com/audio2.wav\n...", lines=5 ), ], outputs=[ gr.Textbox(label="وضعیت", interactive=False), gr.Audio(label="فایل صوتی ادغام شده", type="filepath") ], title="🎙️ ادغام کننده چندین فایل صوتی از طریق لینک (MP3, WAV و غیره) 🎵", description="لینک فایل‌های صوتی خود را (هر لینک در یک خط جدید یا با کاما جدا شده) در کادر زیر وارد کنید تا به صورت یک فایل **MP3** ادغام شوند.", allow_flagging="never", theme=gr.themes.Soft(), # استفاده از تم جدید Gradio css=""" body { font-family: 'Vazirmatn', sans-serif; direction: rtl; text-align: right; } h1, h2, h3, h4, h5, h6 { text-align: center; } .gr-textbox label, .gr-audio label { text-align: right; width: 100%; display: block; } .gradio-container { max-width: 800px; margin: auto; padding: 20px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 8px; } .gr-button { background-color: #4CAF50; /* Example button color */ color: white; } """ ) # تابع راه اندازی: if __name__ == "__main__": iface.launch(share=True)