Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,111 +4,127 @@ import requests
|
|
4 |
import os
|
5 |
import uuid
|
6 |
import re # برای پردازش لینکهای ورودی
|
|
|
7 |
|
8 |
# پوشه برای ذخیره فایلهای موقت
|
9 |
-
TEMP_DIR = "
|
10 |
os.makedirs(TEMP_DIR, exist_ok=True) # اطمینان از وجود پوشه
|
11 |
|
12 |
-
def
|
13 |
"""
|
14 |
-
فایل
|
15 |
-
:param url: URL فایل
|
16 |
-
:param
|
17 |
-
:return:
|
18 |
"""
|
19 |
try:
|
20 |
-
response = requests.get(url, stream=True, timeout=30)
|
21 |
-
response.raise_for_status()
|
22 |
|
23 |
content_type = response.headers.get('Content-Type', '')
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
with open(output_path, 'wb') as f:
|
29 |
for chunk in response.iter_content(chunk_size=8192):
|
30 |
-
if chunk:
|
31 |
f.write(chunk)
|
32 |
-
print(f"Successfully downloaded {url} to {output_path}")
|
33 |
-
return
|
34 |
except requests.exceptions.Timeout:
|
35 |
print(f"Error downloading {url}: Request timed out.")
|
36 |
-
return
|
37 |
except requests.exceptions.RequestException as e:
|
38 |
print(f"Error downloading {url}: {e}")
|
39 |
-
return
|
40 |
except Exception as e:
|
41 |
print(f"An unexpected error occurred during download of {url}: {e}")
|
42 |
-
return
|
43 |
|
44 |
-
def
|
45 |
"""
|
46 |
-
چندین فایل
|
47 |
-
:param
|
48 |
-
:param output_file_path: مسیر کامل برای ذخیره فایل ادغام
|
49 |
:return: True اگر ادغام موفق بود، False در غیر این صورت.
|
50 |
"""
|
51 |
-
if not
|
52 |
return False
|
53 |
|
54 |
combined_audio = None
|
55 |
try:
|
56 |
# بارگذاری اولین فایل
|
57 |
-
|
|
|
58 |
|
59 |
# ادغام بقیه فایلها
|
60 |
-
for i, file_path in enumerate(
|
61 |
-
audio = AudioSegment.from_file(file_path
|
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
|
70 |
return False
|
71 |
|
72 |
-
def
|
73 |
"""
|
74 |
-
این تابع اصلی Gradio است که رشتهای از لینکهای
|
75 |
آنها را دانلود و ادغام میکند.
|
76 |
-
:param urls_input: رشتهای شامل لینکهای
|
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 "لطفاً حداقل یک لینک
|
85 |
|
86 |
if len(urls) < 2:
|
87 |
-
return "لطفاً حداقل دو لینک
|
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}...")
|
108 |
-
|
109 |
-
|
|
|
|
|
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
|
123 |
status_message += f"فایلها با موفقیت ادغام شدند. فایل خروجی: {output_file_path}\n"
|
124 |
-
output_audio_path = output_file_path
|
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=
|
145 |
inputs=[
|
146 |
gr.Textbox(
|
147 |
-
label="لینکهای MP3
|
148 |
-
placeholder="مثال:\nhttps://example.com/audio1.mp3\nhttps://example.com/audio2.
|
149 |
-
lines=5
|
150 |
),
|
151 |
],
|
152 |
outputs=[
|
153 |
gr.Textbox(label="وضعیت", interactive=False),
|
154 |
-
gr.Audio(label="فایل
|
155 |
],
|
156 |
-
title="🎙️ ادغام کننده چندین فایل
|
157 |
-
description="
|
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)
|
|
|
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)
|