Spaces:
Sleeping
Sleeping
| import os | |
| from TTS.utils.synthesizer import Synthesizer | |
| import gradio as gr | |
| from huggingface_hub import hf_hub_download | |
| from huggingface_hub import login | |
| import time | |
| # Uncomment for private models if needed | |
| # login(token=os.environ.get("HF_TOKEN")) | |
| # Custom CSS for better styling - بهینهسازی شده با طراحی حرفهایتر دسکتاپ | |
| custom_css = """ | |
| /* فایل CSS مدرن و حرفهای برای سیستم تبدیل متن فارسی به گفتار - نسخه دسکتاپ 3.0 */ | |
| @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap'); | |
| :root { | |
| /* پالت رنگی جدید - ترکیب آبی و بنفش حرفهای */ | |
| --primary-color: #4f46e5; | |
| --primary-light: #818cf8; | |
| --primary-dark: #3730a3; | |
| --primary-gradient: linear-gradient(135deg, #4f46e5, #6366f1); | |
| --primary-glass: rgba(79, 70, 229, 0.1); | |
| /* رنگهای ثانویه - طیف سبز آبی برای تضاد */ | |
| --secondary-color: #0ea5e9; | |
| --secondary-light: #7dd3fc; | |
| --secondary-dark: #0369a1; | |
| --secondary-gradient: linear-gradient(135deg, #0ea5e9, #38bdf8); | |
| /* رنگهای خنثی - طیف خاکستری سرد برای رابط دسکتاپ */ | |
| --background-color: #f8fafc; | |
| --surface-color: #ffffff; | |
| --text-color: #334155; | |
| --text-light: #64748b; | |
| --text-dark: #1e293b; | |
| --heading-color: #0f172a; | |
| --border-color: #e2e8f0; | |
| --divider-color: #f1f5f9; | |
| /* رنگهای وضعیت بهبود یافته */ | |
| --success-color: #10b981; | |
| --success-light: #d1fae5; | |
| --warning-color: #f59e0b; | |
| --warning-light: #fef3c7; | |
| --error-color: #ef4444; | |
| --error-light: #fee2e2; | |
| --info-color: #3b82f6; | |
| --info-light: #dbeafe; | |
| /* سایههای بهبود یافته برای حالت دسکتاپ */ | |
| --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05); | |
| --shadow-sm: 0 2px 4px rgba(15, 23, 42, 0.07); | |
| --shadow-md: 0 4px 8px rgba(15, 23, 42, 0.08); | |
| --shadow-lg: 0 12px 24px rgba(15, 23, 42, 0.09); | |
| --shadow-xl: 0 20px 40px rgba(15, 23, 42, 0.1); | |
| --shadow-focus: 0 0 0 3px rgba(79, 70, 229, 0.3); | |
| /* انیمیشنها */ | |
| --transition-fast: all 0.2s ease; | |
| --transition: all 0.3s ease; | |
| --transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); | |
| /* اندازههای گوشهها */ | |
| --border-radius-sm: 8px; | |
| --border-radius: 12px; | |
| --border-radius-lg: 16px; | |
| --border-radius-xl: 24px; | |
| --border-radius-full: 9999px; | |
| /* اندازه فاصلهها */ | |
| --spacing-xs: 0.25rem; | |
| --spacing-sm: 0.5rem; | |
| --spacing-md: 1rem; | |
| --spacing-lg: 1.5rem; | |
| --spacing-xl: 2rem; | |
| --spacing-2xl: 3rem; | |
| } | |
| /* تنظیمات پایه */ | |
| body { | |
| font-family: 'Vazirmatn', system-ui, -apple-system, sans-serif; | |
| background-color: var(--background-color); | |
| margin: 0; | |
| padding: 0; | |
| color: var(--text-color); | |
| line-height: 1.6; | |
| } | |
| /* کانتینر اصلی - طراحی دسکتاپ */ | |
| .gradio-container { | |
| background: linear-gradient(135deg, #f0f9ff 0%, #e6f2ff 100%); | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| color: var(--text-color); | |
| max-width: 1400px !important; | |
| margin: 0 auto !important; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* سربرگ اصلی - سبک دسکتاپ */ | |
| .main-header { | |
| color: var(--heading-color); | |
| text-align: center; | |
| margin: 2rem 0; | |
| font-size: 2.8rem; | |
| font-weight: 800; | |
| line-height: 1.2; | |
| letter-spacing: -0.025em; | |
| text-shadow: 0px 2px 4px rgba(0,0,0,0.1); | |
| position: relative; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| } | |
| .main-header:after { | |
| content: ""; | |
| display: block; | |
| width: 140px; | |
| height: 5px; | |
| background: var(--primary-gradient); | |
| margin: 1rem auto; | |
| border-radius: var(--border-radius-full); | |
| } | |
| /* لوگو و آیکونها */ | |
| .logo-area { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .app-logo { | |
| font-size: 2rem; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| color: transparent; | |
| font-weight: 700; | |
| } | |
| /* کانتینر اصلی محتوا - طراحی دسکتاپ */ | |
| .app-container { | |
| display: flex; | |
| flex-direction: column; | |
| max-width: 1200px; | |
| margin: 0 auto 2.5rem; | |
| padding: 0; | |
| flex: 1; | |
| } | |
| .desktop-layout { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 2rem; | |
| align-items: start; | |
| } | |
| /* پنل اصلی */ | |
| .main-panel { | |
| background-color: rgba(255, 255, 255, 0.95); | |
| border-radius: var(--border-radius-lg); | |
| box-shadow: var(--shadow-xl); | |
| backdrop-filter: blur(15px); | |
| border: 1px solid rgba(255, 255, 255, 0.7); | |
| padding: 2.5rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| /* افکت تزئینی پسزمینه */ | |
| .main-panel:before { | |
| content: ""; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: radial-gradient(circle, var(--primary-glass) 0%, transparent 70%); | |
| opacity: 0.5; | |
| z-index: 0; | |
| pointer-events: none; | |
| } | |
| /* نوار عنوان پنلها */ | |
| .panel-title { | |
| background: var(--primary-gradient); | |
| color: white; | |
| padding: 1rem 1.5rem; | |
| border-radius: var(--border-radius) var(--border-radius) 0 0; | |
| font-weight: 600; | |
| font-size: 1.1rem; | |
| margin: -2.5rem -2.5rem 1.5rem -2.5rem; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| .panel-title-icon { | |
| font-size: 1.2rem; | |
| margin-left: 0.5rem; | |
| } | |
| /* فوتر */ | |
| .footer { | |
| text-align: center; | |
| margin-top: 3rem; | |
| color: var(--text-light); | |
| font-size: 0.95rem; | |
| line-height: 1.6; | |
| padding: 0.75rem; | |
| position: relative; | |
| z-index: 1; | |
| background-color: rgba(255, 255, 255, 0.5); | |
| backdrop-filter: blur(10px); | |
| border-top: 1px solid var(--border-color); | |
| } | |
| /* تنظیمات متن فارسی */ | |
| textarea, .label, #text-input { | |
| text-align: right !important; | |
| direction: rtl !important; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| } | |
| .label { | |
| font-size: 1.1rem !important; | |
| font-weight: 600 !important; | |
| color: var(--heading-color) !important; | |
| margin-bottom: 0.75rem !important; | |
| display: block; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| } | |
| /* استایل دکمهها */ | |
| button.primary { | |
| background: var(--primary-gradient) !important; | |
| border: none !important; | |
| border-radius: var(--border-radius) !important; | |
| color: white !important; | |
| font-weight: 600 !important; | |
| font-size: 1.05rem !important; | |
| padding: 0.8rem 1.75rem !important; | |
| transition: var(--transition) !important; | |
| box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3) !important; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| button.primary:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 8px 16px rgba(79, 70, 229, 0.4) !important; | |
| background: linear-gradient(45deg, #3730a3, #4f46e5) !important; | |
| } | |
| button.primary:active { | |
| transform: translateY(0) !important; | |
| box-shadow: 0 2px 8px rgba(79, 70, 229, 0.4) !important; | |
| } | |
| /* افکت موج دکمه */ | |
| button.primary:after { | |
| content: ""; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 5px; | |
| height: 5px; | |
| background: rgba(255, 255, 255, 0.7); | |
| opacity: 0; | |
| border-radius: 100%; | |
| transform: scale(1, 1) translate(-50%); | |
| transform-origin: 50% 50%; | |
| } | |
| button.primary:focus:not(:active)::after { | |
| animation: ripple 1s ease-out; | |
| } | |
| @keyframes ripple { | |
| 0% { | |
| transform: scale(0, 0); | |
| opacity: 0.5; | |
| } | |
| 20% { | |
| transform: scale(25, 25); | |
| opacity: 0.3; | |
| } | |
| 100% { | |
| opacity: 0; | |
| transform: scale(40, 40); | |
| } | |
| } | |
| /* پنلهای ورودی و خروجی */ | |
| .input-panel, .output-panel { | |
| background-color: var(--surface-color); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| border: 1px solid var(--border-color); | |
| transition: var(--transition); | |
| box-shadow: var(--shadow-sm); | |
| position: relative; | |
| z-index: 2; | |
| } | |
| .input-panel:hover, .output-panel:hover { | |
| box-shadow: var(--shadow-md); | |
| border-color: var(--primary-light); | |
| } | |
| /* کادر متن */ | |
| .input-text textarea { | |
| border: 2px solid var(--border-color) !important; | |
| border-radius: var(--border-radius) !important; | |
| padding: 1.25rem !important; | |
| transition: var(--transition) !important; | |
| font-size: 1.05rem !important; | |
| background-color: rgba(255, 255, 255, 0.8) !important; | |
| box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.03) !important; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| color: var(--text-color) !important; | |
| line-height: 1.8 !important; | |
| } | |
| .input-text textarea:focus { | |
| border-color: var(--primary-color) !important; | |
| box-shadow: var(--shadow-focus), inset 0 2px 4px rgba(0, 0, 0, 0.03) !important; | |
| background-color: rgba(255, 255, 255, 1) !important; | |
| outline: none !important; | |
| } | |
| /* رنگ متن در کانتینر */ | |
| .gradio-container textarea { | |
| color: var(--text-color) !important; | |
| } | |
| /* اسلایدر سرعت */ | |
| .speed-slider-container { | |
| background: linear-gradient(to right, rgba(255,255,255,0.9), rgba(240,246,255,0.9)); | |
| border-radius: var(--border-radius); | |
| padding: 1.25rem; | |
| margin-top: 1.5rem; | |
| border: 1px solid var(--border-color); | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .speed-slider input[type="range"] { | |
| height: 8px !important; | |
| border-radius: var(--border-radius-full) !important; | |
| background: linear-gradient(90deg, var(--primary-light), var(--secondary-light)) !important; | |
| } | |
| .speed-slider input[type="range"]::-webkit-slider-thumb { | |
| background: var(--primary-gradient) !important; | |
| border: 3px solid white !important; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) !important; | |
| height: 20px !important; | |
| width: 20px !important; | |
| border-radius: 50% !important; | |
| } | |
| .speed-slider .label { | |
| font-size: 1rem !important; | |
| color: var(--text-color) !important; | |
| font-weight: 500 !important; | |
| } | |
| /* پنل مثالها */ | |
| .examples-panel { | |
| background-color: rgba(248, 250, 252, 0.8); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| border: 1px solid var(--border-color); | |
| margin-top: 2rem; | |
| position: relative; | |
| z-index: 2; | |
| } | |
| /* پنل وضعیت با کلاسهای مختلف */ | |
| .status-panel { | |
| background: linear-gradient(135deg, #f8fafc, #f1f5f9); | |
| border-radius: var(--border-radius); | |
| padding: 1rem; | |
| margin: 0 0 1.5rem 0; | |
| text-align: center; | |
| font-weight: 500; | |
| border-right: 4px solid var(--primary-color); | |
| transition: var(--transition); | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| position: relative; | |
| overflow: hidden; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .status-success { | |
| color: var(--success-color) !important; | |
| background: linear-gradient(135deg, #f0fdf4, #dcfce7) !important; | |
| border-right-color: var(--success-color) !important; | |
| } | |
| .status-error { | |
| color: var(--error-color) !important; | |
| background: linear-gradient(135deg, #fef2f2, #fee2e2) !important; | |
| border-right-color: var(--error-color) !important; | |
| } | |
| .status-processing { | |
| color: var(--info-color) !important; | |
| background: linear-gradient(135deg, #eff6ff, #dbeafe) !important; | |
| border-right-color: var(--info-color) !important; | |
| background-size: 200% 200% !important; | |
| animation: wave 2s ease infinite !important; | |
| } | |
| /* استایل پخشکننده صوتی */ | |
| audio { | |
| border-radius: var(--border-radius) !important; | |
| background: linear-gradient(to right, #dbeafe, #e0e7ff) !important; | |
| box-shadow: var(--shadow-sm) !important; | |
| width: 100% !important; | |
| transition: var(--transition) !important; | |
| } | |
| audio:hover { | |
| box-shadow: var(--shadow-md) !important; | |
| } | |
| /* استایل دکمههای مثال */ | |
| .examples-panel button { | |
| border: 1px solid #e2e8f0 !important; | |
| background: rgba(255, 255, 255, 0.8) !important; | |
| border-radius: var(--border-radius-sm) !important; | |
| transition: var(--transition-fast) !important; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| color: var(--text-color) !important; | |
| } | |
| .examples-panel button:hover { | |
| background: white !important; | |
| border-color: var(--primary-light) !important; | |
| transform: translateY(-2px) !important; | |
| box-shadow: var(--shadow-sm) !important; | |
| color: var(--primary-dark) !important; | |
| } | |
| /* طرح دکمه بزرگ */ | |
| .big-button { | |
| font-size: 1.1rem !important; | |
| padding: 1rem 2rem !important; | |
| width: 100% !important; | |
| margin-top: 1rem !important; | |
| text-align: center !important; | |
| } | |
| /* نوار پیشرفت */ | |
| .progress-bar { | |
| height: 8px; | |
| background-color: var(--border-color); | |
| border-radius: var(--border-radius-full); | |
| margin: 0.5rem 0; | |
| overflow: hidden; | |
| } | |
| .progress-bar-fill { | |
| height: 100%; | |
| background: var(--primary-gradient); | |
| border-radius: var(--border-radius-full); | |
| transition: width 0.3s ease; | |
| width: 0%; | |
| } | |
| /* انیمیشنهای جلوهای */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(15px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .desktop-layout { | |
| animation: fadeIn 0.7s ease-out; | |
| } | |
| /* افکت موج برای پردازش صوتی */ | |
| @keyframes wave { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| /* حالت شب و روز */ | |
| .theme-switch { | |
| position: absolute; | |
| top: 1rem; | |
| right: 1rem; | |
| background: var(--secondary-gradient); | |
| color: white; | |
| width: 2.5rem; | |
| height: 2.5rem; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: var(--shadow-md); | |
| z-index: 10; | |
| transition: var(--transition); | |
| } | |
| .theme-switch:hover { | |
| transform: scale(1.1); | |
| } | |
| /* پشتیبانی RTL برای تمام عناصر UI */ | |
| .gradio-container [dir="rtl"], | |
| .gradio-container [style*="direction: rtl"] { | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| } | |
| /* اصلاح المانهای مارکداون */ | |
| .prose p, .prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 { | |
| direction: rtl !important; | |
| text-align: right !important; | |
| font-family: 'Vazirmatn', system-ui, sans-serif !important; | |
| color: var(--text-color) !important; | |
| } | |
| /* استایل لینکها */ | |
| a { | |
| color: var(--primary-color) !important; | |
| text-decoration: none !important; | |
| transition: var(--transition-fast) !important; | |
| font-weight: 500 !important; | |
| } | |
| a:hover { | |
| color: var(--primary-dark) !important; | |
| text-decoration: underline !important; | |
| } | |
| /* کارتهای اطلاعات */ | |
| .info-card { | |
| background: linear-gradient(135deg, rgba(255,255,255,0.9), rgba(240,246,255,0.9)); | |
| border-radius: var(--border-radius); | |
| padding: 1.25rem; | |
| margin-bottom: 1.5rem; | |
| border: 1px solid var(--border-color); | |
| transition: var(--transition); | |
| position: relative; | |
| z-index: 2; | |
| } | |
| .info-card:hover { | |
| box-shadow: var(--shadow-md); | |
| border-color: var(--primary-light); | |
| } | |
| /* واکنشگرایی برای نمایشگرهای مختلف */ | |
| @media (max-width: 1024px) { | |
| .desktop-layout { | |
| grid-template-columns: 1fr; | |
| } | |
| .main-panel { | |
| padding: 2rem; | |
| } | |
| .panel-title { | |
| margin: -2rem -2rem 1.5rem -2rem; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .main-panel { | |
| padding: 1.5rem; | |
| } | |
| .panel-title { | |
| margin: -1.5rem -1.5rem 1.25rem -1.5rem; | |
| padding: 0.75rem 1.25rem; | |
| } | |
| .main-header { | |
| font-size: 2rem; | |
| margin: 1.5rem 0; | |
| } | |
| .label { | |
| font-size: 1rem !important; | |
| } | |
| .input-panel, .output-panel { | |
| padding: 1.25rem; | |
| } | |
| } | |
| /* بهینهسازی برای نمایشگرهای کوچک */ | |
| @media (max-width: 480px) { | |
| .main-panel { | |
| padding: 1.25rem; | |
| border-radius: var(--border-radius); | |
| } | |
| .panel-title { | |
| margin: -1.25rem -1.25rem 1rem -1.25rem; | |
| padding: 0.75rem 1rem; | |
| } | |
| .main-header { | |
| font-size: 1.5rem; | |
| } | |
| button.primary { | |
| width: 100%; | |
| padding: 0.7rem 1rem !important; | |
| } | |
| } | |
| /* اصلاحات سبک برای گردیو 4 */ | |
| .gr-padded { | |
| padding: var(--spacing-lg) !important; | |
| } | |
| .gr-gap { | |
| gap: var(--spacing-md) !important; | |
| } | |
| .gradio-button { | |
| border-radius: var(--border-radius) !important; | |
| } | |
| /* اصلاح فاصلهها */ | |
| .gr-panel > .gr-block { | |
| margin-bottom: var(--spacing-md) !important; | |
| } | |
| .gr-form > .gr-block { | |
| margin-bottom: var(--spacing-lg) !important; | |
| } | |
| /* بهبود المانهای گردیو 4 */ | |
| .gr-box, .gr-block, .gr-input, .gr-textbox { | |
| border-radius: var(--border-radius) !important; | |
| } | |
| .gr-form { | |
| border: none !important; | |
| background: transparent !important; | |
| } | |
| .gr-accordion { | |
| border-radius: var(--border-radius) !important; | |
| overflow: hidden !important; | |
| } | |
| .gr-accordion-title { | |
| font-weight: 600 !important; | |
| color: var(--heading-color) !important; | |
| } | |
| .gr-panel { | |
| border-radius: var(--border-radius) !important; | |
| } | |
| /* استایلهای خاص برای گردیو 4 */ | |
| .gr-input, .gr-textbox { | |
| transition: var(--transition) !important; | |
| } | |
| .gr-input:focus, .gr-textbox:focus { | |
| border-color: var(--primary-color) !important; | |
| box-shadow: var(--shadow-focus) !important; | |
| } | |
| .gr-padded-container { | |
| padding: var(--spacing-lg) !important; | |
| } | |
| .gr-compact { | |
| margin: 0 !important; | |
| } | |
| .gr-hover-container:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: var(--shadow-md) !important; | |
| } | |
| .gr-interface { | |
| margin: 0 !important; | |
| } | |
| /* برخی از اصلاحات مهم برای Gradio 4 */ | |
| .gr-form { | |
| border: none !important; | |
| background: transparent !important; | |
| } | |
| .gr-form-input { | |
| border-radius: var(--border-radius) !important; | |
| transition: var(--transition) !important; | |
| } | |
| .gr-form-input:focus { | |
| border-color: var(--primary-color) !important; | |
| box-shadow: var(--shadow-focus) !important; | |
| } | |
| .gr-block { | |
| margin-bottom: var(--spacing-md) !important; | |
| } | |
| .gr-input-label { | |
| color: var(--heading-color) !important; | |
| font-weight: 600 !important; | |
| margin-bottom: var(--spacing-xs) !important; | |
| } | |
| .gr-interface { | |
| margin: 0 !important; | |
| } | |
| /* اصلاحات اضافی */ | |
| svg.gr-arrow { | |
| fill: var(--primary-color) !important; | |
| } | |
| .dark-mode { | |
| --background-color: #0f172a; | |
| --surface-color: #1e293b; | |
| --text-color: #e2e8f0; | |
| --text-light: #94a3b8; | |
| --text-dark: #f8fafc; | |
| --heading-color: #f1f5f9; | |
| --border-color: #334155; | |
| --divider-color: #1e293b; | |
| } | |
| """ | |
| def load_synthesizer(): | |
| # Update status message | |
| status = "در حال بارگذاری مدل... لطفاً منتظر بمانید" | |
| try: | |
| # Download model files from Hugging Face Hub | |
| model_path = hf_hub_download( | |
| repo_id="QomSSLab/vits-fa-voice", | |
| filename="best_model.pth", | |
| cache_dir="models" | |
| ) | |
| config_path = hf_hub_download( | |
| repo_id="QomSSLab/vits-fa-voice", | |
| filename="config.json", | |
| cache_dir="models" | |
| ) | |
| # Create synthesizer | |
| synthesizer = Synthesizer( | |
| tts_checkpoint=model_path, | |
| tts_config_path=config_path, | |
| use_cuda=False # Usually no GPU in free Spaces | |
| ) | |
| status = "✅ مدل با موفقیت بارگذاری شد! اکنون میتوانید از سیستم استفاده کنید." | |
| return synthesizer, status | |
| except Exception as e: | |
| error_msg = f"خطا در بارگذاری مدل: {str(e)}" | |
| status = f"❌ {error_msg}" | |
| raise RuntimeError(error_msg) | |
| def tts(text, speed): | |
| if not text.strip(): | |
| return None, "لطفاً متنی وارد کنید.", "❌ لطفاً متنی وارد کنید." | |
| try: | |
| status = "در حال تبدیل متن به گفتار..." | |
| # Generate speech | |
| wav = synthesizer.tts(text, speed=speed) | |
| output_path = "output.wav" | |
| synthesizer.save_wav(wav, output_path) | |
| status = "✅ صدا با موفقیت تولید شد!" | |
| return output_path, "تبدیل با موفقیت انجام شد.", status | |
| except Exception as e: | |
| error_msg = f"خطا در تولید صدا: {str(e)}" | |
| status = f"❌ {error_msg}" | |
| return None, error_msg, status | |
| # JavaScript function for updating status classes | |
| def update_status_classes(status_text): | |
| # This will be used with the gradio.js code | |
| return status_text | |
| # Create the interface with improved layout and professional design | |
| with gr.Blocks(css=custom_css) as demo: | |
| with gr.Column(elem_classes="container"): | |
| gr.Markdown("# سامانه تبدیل متن فارسی به گفتار", elem_classes="main-header") | |
| # Status area | |
| with gr.Column(elem_classes="status-panel") as status_container: | |
| status_output = gr.Markdown("در حال آمادهسازی سیستم...", elem_id="status") | |
| # Input panel | |
| with gr.Column(elem_classes="input-panel"): | |
| gr.Markdown("### متن ورودی", elem_classes="label") | |
| text_input = gr.Textbox( | |
| placeholder="متن فارسی خود را اینجا وارد کنید...", | |
| lines=5, | |
| label="", | |
| elem_id="text-input", | |
| elem_classes="input-text" | |
| ) | |
| with gr.Row(): | |
| speed_slider = gr.Slider( | |
| minimum=0.5, | |
| maximum=2.0, | |
| value=1.0, | |
| step=0.1, | |
| label="سرعت گفتار", | |
| elem_classes="speed-slider" | |
| ) | |
| submit_btn = gr.Button("تبدیل به گفتار", variant="primary", elem_classes="primary") | |
| # Output panel | |
| with gr.Column(elem_classes="output-panel"): | |
| gr.Markdown("### خروجی صوتی", elem_classes="label") | |
| output_audio = gr.Audio(label="") | |
| result_text = gr.Markdown("") | |
| # Examples panel | |
| with gr.Column(elem_classes="examples-panel"): | |
| gr.Markdown("### نمونههای متنی", elem_classes="label") | |
| examples = gr.Examples( | |
| examples=[ | |
| ["سَلامْ دُنیا این یک آزمایش برای سیستم تبدیل متن به گفتارِ فارِسی است."], | |
| ["اِمروز هوا بسیار خوب است و من احساسِ شادی میکنم."], | |
| ["فَناوریِ هوشَ مصنوعیْ به سرعت در حالِ پیشرفت است و به زودی در تمام جنبههای زْندِگِیَ ما حضور خواهد داشت."] | |
| ], | |
| inputs=text_input, | |
| label="نمونههای متنی را امتحان کنید" | |
| ) | |
| gr.Markdown( | |
| "**راهنما**: متن فارسی خود را در کادر بالا وارد کنید و دکمه تبدیل را فشار دهید. " | |
| "میتوانید سرعت گفتار را با استفاده از نوار لغزنده تنظیم کنید.", | |
| elem_classes="footer" | |
| ) | |
| gr.Markdown( | |
| "توسعه داده شده با استفاده از مدل VITS فارسی | [QomSSLab/vits-fa-voice](https://huggingface.co/QomSSLab/vits-fa-voice)", | |
| elem_classes="footer" | |
| ) | |
| # Add JavaScript for dynamic class changes - Gradio 4.0+ way | |
| demo.load(js=""" | |
| function updateStatusClass(status_text) { | |
| const statusPanel = document.querySelector('.status-panel'); | |
| if (!statusPanel) return; | |
| // Remove all status classes | |
| statusPanel.classList.remove('status-success', 'status-error', 'status-processing'); | |
| // Add appropriate class based on status content | |
| if (status_text.includes('✅')) { | |
| statusPanel.classList.add('status-success'); | |
| } else if (status_text.includes('❌')) { | |
| statusPanel.classList.add('status-error'); | |
| } else if (status_text.includes('در حال')) { | |
| statusPanel.classList.add('status-processing'); | |
| } | |
| } | |
| // Monitor changes to the status element | |
| const observer = new MutationObserver((mutations) => { | |
| mutations.forEach((mutation) => { | |
| if (mutation.type === 'childList') { | |
| const statusEl = document.querySelector('#status'); | |
| if (statusEl) { | |
| updateStatusClass(statusEl.textContent); | |
| } | |
| } | |
| }); | |
| }); | |
| // Start observing when the DOM is ready | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const statusEl = document.querySelector('#status'); | |
| if (statusEl) { | |
| observer.observe(statusEl, { childList: true, subtree: true }); | |
| // Initial update | |
| updateStatusClass(statusEl.textContent); | |
| } | |
| }); | |
| """) | |
| # Initialize the synthesizer on app startup | |
| synthesizer = None | |
| def init_synthesizer(): | |
| global synthesizer | |
| try: | |
| synthesizer, status = load_synthesizer() | |
| return status_output.update(status) | |
| except Exception as e: | |
| error_status = f"❌ خطا در بارگذاری مدل: {str(e)}" | |
| return status_output.update(error_status) | |
| # Define the tts_wrapper to handle status updates | |
| def tts_wrapper(text, speed): | |
| audio, result, status = tts(text, speed) | |
| return audio, result, status | |
| # Connect the function to the button | |
| submit_btn.click( | |
| fn=tts_wrapper, | |
| inputs=[text_input, speed_slider], | |
| outputs=[output_audio, result_text, status_output] | |
| ) | |
| # Launch the interface | |
| demo.launch() | |