Spaces:
Running
Running
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) | |