Spaces:
Running
Running
import gradio as gr | |
from pydub import AudioSegment | |
import requests | |
import os | |
import uuid | |
import re # برای پردازش لینکهای ورودی | |
# پوشه برای ذخیره فایلهای موقت | |
TEMP_DIR = "temp_mp3_files" | |
os.makedirs(TEMP_DIR, exist_ok=True) # اطمینان از وجود پوشه | |
def download_mp3(url: str, output_path: str) -> bool: | |
""" | |
فایل MP3 را از URL دانلود میکند. | |
:param url: URL فایل MP3. | |
:param output_path: مسیر کامل برای ذخیره فایل دانلود شده. | |
:return: True اگر دانلود موفق بود، False در غیر این صورت. | |
""" | |
try: | |
response = requests.get(url, stream=True, timeout=30) # افزودن timeout | |
response.raise_for_status() # برای HTTP errors (4xx or 5xx) | |
content_type = response.headers.get('Content-Type', '') | |
if 'audio' not in content_type and 'mpeg' not in content_type: | |
print(f"URL: {url} does not seem to point to an audio file. Content-Type: {content_type}") | |
return False | |
with open(output_path, 'wb') as f: | |
for chunk in response.iter_content(chunk_size=8192): | |
if chunk: # filter out keep-alive new chunks | |
f.write(chunk) | |
print(f"Successfully downloaded {url} to {output_path}") | |
return True | |
except requests.exceptions.Timeout: | |
print(f"Error downloading {url}: Request timed out.") | |
return False | |
except requests.exceptions.RequestException as e: | |
print(f"Error downloading {url}: {e}") | |
return False | |
except Exception as e: | |
print(f"An unexpected error occurred during download of {url}: {e}") | |
return False | |
def merge_mp3_files_sequential(mp3_file_paths: list[str], output_file_path: str) -> bool: | |
""" | |
چندین فایل MP3 را به ترتیب ادغام میکند. | |
:param mp3_file_paths: لیستی از مسیرهای فایلهای MP3. | |
:param output_file_path: مسیر کامل برای ذخیره فایل ادغام شده. | |
:return: True اگر ادغام موفق بود، False در غیر این صورت. | |
""" | |
if not mp3_file_paths: | |
return False | |
combined_audio = None | |
try: | |
# بارگذاری اولین فایل | |
combined_audio = AudioSegment.from_file(mp3_file_paths[0], format="mp3") | |
# ادغام بقیه فایلها | |
for i, file_path in enumerate(mp3_file_paths[1:]): | |
audio = AudioSegment.from_file(file_path, format="mp3") | |
combined_audio += audio | |
print(f"Merged {file_path}") | |
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 MP3 files: {e}") | |
return False | |
def process_mp3_links_gradio(urls_input: str) -> tuple[str, str | None]: | |
""" | |
این تابع اصلی Gradio است که رشتهای از لینکهای MP3 را دریافت کرده، | |
آنها را دانلود و ادغام میکند. | |
:param urls_input: رشتهای شامل لینکهای MP3، جدا شده با خط جدید یا کاما. | |
:return: یک تاپل شامل پیام وضعیت و مسیر فایل خروجی (یا None). | |
""" | |
# پاکسازی و تقسیم لینکها | |
# حذف فضای خالی اطراف لینکها و فیلتر کردن لینکهای خالی | |
urls = [url.strip() for url in re.split(r'[\n,]+', urls_input) if url.strip()] | |
if not urls: | |
return "لطفاً حداقل یک لینک MP3 معتبر وارد کنید.", None | |
if len(urls) < 2: | |
return "لطفاً حداقل دو لینک MP3 برای ادغام وارد کنید.", None | |
status_message = "" | |
output_audio_path = None | |
downloaded_files_paths = [] # برای نگهداری مسیر فایلهای دانلود شده | |
temp_file_mapping = {} # URL -> temp_path | |
# برای تولید یک 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): | |
temp_file_name = f"temp_{i+1}_{uuid.uuid4().hex}.mp3" | |
temp_file_path = os.path.join(request_temp_dir, temp_file_name) | |
temp_file_mapping[url] = temp_file_path # ذخیره mapping برای حذف آسان | |
status_message += f"در حال دانلود فایل {i+1} از: {url}\n" | |
gr.Info(f"Downloading file {i+1}...") # نمایش پیام در UI Gradio | |
if download_mp3(url, temp_file_path): | |
downloaded_files_paths.append(temp_file_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" | |
output_file_path = os.path.join(request_temp_dir, output_file_name) | |
if merge_mp3_files_sequential(downloaded_files_paths, output_file_path): | |
status_message += f"فایلها با موفقیت ادغام شدند. فایل خروجی: {output_file_path}\n" | |
output_audio_path = output_file_path # مسیر فایل برای Gradio | |
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_mp3_links_gradio, | |
inputs=[ | |
gr.Textbox( | |
label="لینکهای MP3 (هر لینک در یک خط یا با کاما جدا کنید)", | |
placeholder="مثال:\nhttps://example.com/audio1.mp3\nhttps://example.com/audio2.mp3\n...", | |
lines=5 # امکان ورود چند خط | |
), | |
], | |
outputs=[ | |
gr.Textbox(label="وضعیت", interactive=False), | |
gr.Audio(label="فایل MP3 ادغام شده", type="filepath") | |
], | |
title="🎙️ ادغام کننده چندین فایل MP3 از طریق لینک 🎵", | |
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) # share=True برای دسترسی عمومی در هاگینگفیس یا از طریق لینک موقت | |