File size: 9,728 Bytes
319ea69
c296f8a
cd88e5f
39ac26a
cd88e5f
 
1d71064
319ea69
cd88e5f
1d71064
cd88e5f
 
1d71064
cd88e5f
1d71064
 
 
 
cd88e5f
 
1d71064
 
cd88e5f
 
1d71064
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cd88e5f
 
 
1d71064
cd88e5f
1d71064
 
cd88e5f
 
1d71064
cd88e5f
 
1d71064
cd88e5f
 
1d71064
cd88e5f
1d71064
cd88e5f
1d71064
 
 
cd88e5f
 
1d71064
cd88e5f
 
 
c296f8a
cd88e5f
1d71064
 
cd88e5f
 
1d71064
 
cd88e5f
 
 
1d71064
cd88e5f
 
 
319ea69
1d71064
cd88e5f
 
1d71064
cd88e5f
1d71064
cd88e5f
1d71064
cd88e5f
 
 
 
 
 
1d71064
cd88e5f
 
1d71064
cd88e5f
 
 
1d71064
cd88e5f
 
 
 
 
 
 
 
 
1d71064
 
 
 
 
cd88e5f
1d71064
cd88e5f
 
 
 
 
 
 
1d71064
cd88e5f
 
1d71064
cd88e5f
1d71064
cd88e5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d71064
cd88e5f
 
1d71064
 
 
cd88e5f
 
 
 
1d71064
cd88e5f
1d71064
 
cd88e5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a12f33
1d71064
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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)