Update index.html
Browse files- index.html +76 -40
index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>تولید صدای هوشمند و پادکست | AI Sada</title>
|
| 7 |
-
<meta name="description" content="با AI Sada، متن فارسی خود را به صدایی طبیعی و با کیفیت استودیویی تبدیل کنید.
|
| 8 |
<!-- Font Awesome for Icons -->
|
| 9 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 10 |
|
|
@@ -23,9 +23,7 @@
|
|
| 23 |
--radius-card: 24px; --radius-btn: 14px; --radius-input: 12px;
|
| 24 |
--transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
| 25 |
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
| 26 |
-
--transition-bounce: all 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
| 27 |
--card-bg: #FFFFFF;
|
| 28 |
-
--danger: #E53E3E;
|
| 29 |
}
|
| 30 |
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
|
| 31 |
@keyframes fadeInOut { 0% { opacity: 0; transform: translateY(10px); } 10% { opacity: 1; transform: translateY(0); } 90% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-10px); } }
|
|
@@ -39,7 +37,7 @@
|
|
| 39 |
@keyframes pulse-soft { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
|
| 40 |
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 40% {transform: translateY(-15px);} 60% {transform: translateY(-7px);} }
|
| 41 |
|
| 42 |
-
/* PODCAST STUDIO VIEW */
|
| 43 |
.podcast-studio-container { width: 100%; animation: fadeIn 0.5s ease-out; }
|
| 44 |
.podcast-studio-container .form-group-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; }
|
| 45 |
.podcast-studio-container #project-speakers-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1.5rem; text-align: center; }
|
|
@@ -96,16 +94,21 @@
|
|
| 96 |
.podcast-studio-container .loading-state-wrapper .loading-text { font-size: 0.85em; font-weight: 600; color: var(--accent-primary); white-space: nowrap; transition: all 0.3s; }
|
| 97 |
.podcast-studio-container .turn-retry-btn { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; background: none; border: 1px solid var(--input-border); color: var(--text-secondary); padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); cursor: pointer; transition: var(--transition-smooth); margin-right: auto; display: flex; align-items: center; gap: 6px; }
|
| 98 |
.podcast-studio-container .turn-retry-btn:hover { border-color: var(--accent-secondary); color: var(--accent-secondary); background: var(--accent-secondary-glow); }
|
|
|
|
| 99 |
.podcast-studio-container .replace-success-message, .podcast-studio-container .main-update-message { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); white-space: nowrap; display: none; align-items: center; justify-content: center; margin-right: auto; }
|
| 100 |
.podcast-studio-container .replace-success-message { background-color: var(--accent-secondary-glow); color: var(--accent-secondary); }
|
| 101 |
.podcast-studio-container .main-update-message { background: linear-gradient(90deg, #e6fffa, #b2f5ea); color: #2c7a7b; border: 1px solid #81e6d9; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
| 102 |
|
| 103 |
.beautiful-download-btn { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; max-width: 400px; margin: 1.5rem auto 0.5rem auto; padding: 16px 24px; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); color: #fff; border: none; border-radius: 14px; font-family: var(--app-font); font-weight: 800; font-size: 1.1em; cursor: pointer; position: relative; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(37, 99, 235, 0.1), 0 2px 4px -1px rgba(37, 99, 235, 0.06); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-bottom: 4px solid #1d4ed8; }
|
| 104 |
.beautiful-download-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 15px -3px rgba(37, 99, 235, 0.2), 0 4px 6px -2px rgba(37, 99, 235, 0.1); filter: brightness(1.05); }
|
|
|
|
| 105 |
|
| 106 |
#clear-history-btn-podcast { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; max-width: 300px; margin: 1rem auto 0 auto; padding: 12px; background-color: #fff; border: 2px dashed #cbd5e0; color: #718096; font-family: var(--app-font); font-size: 0.95em; font-weight: 700; border-radius: 12px; cursor: pointer; transition: all 0.3s ease; }
|
| 107 |
#clear-history-btn-podcast:hover { border-color: #fc8181; color: #e53e3e; background-color: #fff5f5; transform: translateY(-2px); }
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
| 109 |
/* Modals and Options */
|
| 110 |
#long-text-modal .modal-dialog { max-width: 600px; background: linear-gradient(160deg, #ffffff 0%, #f7f9fc 100%); padding: 3rem 2.5rem; max-height: 90vh; overflow-y: auto; }
|
| 111 |
#long-text-modal .modal-header { border: none; justify-content: center; flex-direction: column; text-align: center; margin-bottom: 1.5rem; }
|
|
@@ -119,6 +122,7 @@
|
|
| 119 |
.option-content { flex-grow: 1; text-align: right; }
|
| 120 |
.option-title { font-weight: 800; font-size: 1rem; color: var(--text-primary); margin-bottom: 2px; }
|
| 121 |
.option-subtitle { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.4; }
|
|
|
|
| 122 |
.special-ai-card { background: linear-gradient(135deg, #f3f4f6 0%, #ffffff 100%); border-color: #d1d5db; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
|
| 123 |
.special-ai-card:hover { border-color: #8b5cf6; background: linear-gradient(135deg, #f5f3ff 0%, #ffffff 100%); box-shadow: 0 10px 20px rgba(139, 92, 246, 0.15); }
|
| 124 |
.special-ai-card .option-icon { background: #ede9fe; color: #8b5cf6; }
|
|
@@ -132,8 +136,10 @@
|
|
| 132 |
.confirm-modal-dialog p { color: var(--text-secondary); margin: 0 0 2rem 0; line-height: 1.7; }
|
| 133 |
.confirm-modal-actions { display: flex; gap: 1rem; justify-content: center; }
|
| 134 |
.confirm-btn, .cancel-btn { font-family: var(--app-font); font-size: 1em; font-weight: 700; padding: 0.8rem 1.5rem; border-radius: var(--radius-btn); border: none; cursor: pointer; transition: all 0.2s ease; flex-grow: 1; }
|
| 135 |
-
.confirm-btn { background-color: #e53e3e; color: white; }
|
| 136 |
-
.
|
|
|
|
|
|
|
| 137 |
|
| 138 |
.ai-podcast-card { grid-column: 1 / -1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 18px !important; display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: space-between !important; padding: 0 1.5rem !important; margin-top: 10px; cursor: pointer; min-height: 90px; }
|
| 139 |
.ai-podcast-card:hover { transform: translateY(-5px) scale(1.02) !important; box-shadow: 0 15px 30px rgba(118, 75, 162, 0.6) !important; }
|
|
@@ -141,10 +147,12 @@
|
|
| 141 |
.ai-podcast-title { font-size: 1.3rem; font-weight: 900; color: white; margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
|
| 142 |
.ai-podcast-subtitle { font-size: 0.9rem; color: rgba(255,255,255,0.9); font-weight: 500; }
|
| 143 |
.ai-podcast-icon { font-size: 2.5rem; margin-left: 1rem; animation: pulse-soft 2s infinite; }
|
|
|
|
|
|
|
| 144 |
|
| 145 |
#ai-podcast-modal .modal-header h2 { background: linear-gradient(90deg, #FF0080, #7928CA); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.8rem; }
|
| 146 |
#ai-podcast-input { width: 100%; min-height: 180px; padding: 1.2rem; border-radius: 16px; border: 2px solid var(--input-border); background-color: var(--input-bg); font-family: var(--app-font); font-size: 1rem; line-height: 1.8; resize: vertical; transition: all 0.3s; margin: 1.5rem 0; }
|
| 147 |
-
#ai-podcast-input:focus { outline: none; border-color: #7928CA; background-color: white; }
|
| 148 |
|
| 149 |
.sample-player-container { position: absolute; top: 50%; left: 4px; transform: translateY(-50%); display: flex; flex-direction: column; gap: 4px; z-index: 6; }
|
| 150 |
.sample-play-btn { width: 24px; height: 24px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; background: linear-gradient(145deg, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.25)); border: 1px solid rgba(255, 255, 255, 0.15); }
|
|
@@ -154,11 +162,11 @@
|
|
| 154 |
.sample-play-btn.playing { background: var(--accent-secondary); }
|
| 155 |
.sample-play-btn.playing .play-icon-svg { display: none; }
|
| 156 |
.sample-play-btn.playing .playing-indicator { display: flex; }
|
| 157 |
-
@keyframes sound-wave { 0% { height: 2px; } 25% { height: 8px; } 50% { height: 4px; } 100% { height: 2px; } }
|
| 158 |
|
| 159 |
#sample-text-display { position: fixed; bottom: 25px; left: 50%; width: 90%; max-width: 600px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 20px; padding: 20px 24px; box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15); z-index: 2000; text-align: right; direction: rtl; display: flex; flex-direction: column; align-items: flex-start; opacity: 0; visibility: hidden; transform: translateX(-50%) translateY(30px) scale(0.95); transition: all 0.5s; }
|
| 160 |
#sample-text-display.active { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0) scale(1); }
|
| 161 |
-
#sample-text-display h4 { margin: 0 0 10px; color: var(--accent-primary); font-size: 0.8em; font-weight: 800; }
|
|
|
|
| 162 |
#sample-text-display p { margin: 0; color: var(--text-primary); font-weight: 500; font-size: 0.9em; line-height: 1.7; }
|
| 163 |
|
| 164 |
#speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 1.5rem; padding-bottom: 0; transition: padding-bottom 0.4s; }
|
|
@@ -249,10 +257,6 @@
|
|
| 249 |
.feature-card h3 { font-size: 1.3em; margin-bottom: 0.5rem; color: var(--text-primary); }
|
| 250 |
.site-footer { text-align: center; padding: 2rem 0; margin-top: 3rem; border-top: 1px solid var(--panel-border); color: var(--text-tertiary); }
|
| 251 |
.site-footer a { color: var(--accent-primary); text-decoration: none; font-weight: 600; }
|
| 252 |
-
.contact-section { background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); padding: 3rem 2rem; margin-top: 4rem; border-radius: var(--radius-card); text-align: center; color: white; box-shadow: var(--shadow-xl); }
|
| 253 |
-
.contact-section h2 { margin: 0 0 1rem 0; font-size: 2em; font-weight: 800; text-shadow: 0 2px 4px rgba(0,0,0,0.2); }
|
| 254 |
-
.contact-section p { margin: 0 0 2rem 0; opacity: 0.9; font-size: 1.1em; }
|
| 255 |
-
.contact-button { display: inline-flex; align-items: center; gap: 12px; padding: 1rem 2rem; background-color: white; color: #1DA1F2; border-radius: var(--radius-btn); text-decoration: none; font-weight: 700; font-size: 1.1em; box-shadow: var(--shadow-lg); transition: all 0.3s ease; }
|
| 256 |
</style>
|
| 257 |
</head>
|
| 258 |
<body>
|
|
@@ -262,7 +266,7 @@
|
|
| 262 |
<div id="standard-view">
|
| 263 |
<header class="app-header">
|
| 264 |
<h1>مولد صدای هوشمند Ai Sada</h1>
|
| 265 |
-
<p>کیفیت استودیو، قدرت هوش مصنوعی. متن فارسی را به صدایی طبیعی و با کیفیت استودیویی تبدیل کنید یا پادکستهای چند نفره بسازید.
|
| 266 |
</header>
|
| 267 |
<main class="main-content">
|
| 268 |
<form id="standard-tts-form" onsubmit="return false;">
|
|
@@ -341,18 +345,22 @@
|
|
| 341 |
<div class="option-card" data-mode="single">
|
| 342 |
<div class="option-icon">👤</div>
|
| 343 |
<div class="option-content"><div class="option-title">با یک گوینده</div><div class="option-subtitle">متن به صورت یکپارچه با گوینده انتخابی شما خوانده میشود.</div></div>
|
|
|
|
| 344 |
</div>
|
| 345 |
<div class="option-card" data-mode="double">
|
| 346 |
<div class="option-icon">👥</div>
|
| 347 |
<div class="option-content"><div class="option-title">با دو گوینده</div><div class="option-subtitle">متن به صورت هوشمند بین دو گوینده تصادفی تقسیم میشود.</div></div>
|
|
|
|
| 348 |
</div>
|
| 349 |
<div class="option-card" data-mode="multi">
|
| 350 |
<div class="option-icon">🎭</div>
|
| 351 |
<div class="option-content"><div class="option-title">با چندین گوینده</div><div class="option-subtitle">هوش مصنوعی متن را تحلیل کرده و با چندین گوینده تصادفی اجرا میکند.</div></div>
|
|
|
|
| 352 |
</div>
|
| 353 |
<div class="option-card special-ai-card" data-mode="ai-podcast">
|
| 354 |
<div class="option-icon">🎙️</div>
|
| 355 |
<div class="option-content"><div class="option-title">تبدیل متن به پادکست</div><div class="option-subtitle">هوش مصنوعی متن شما را به سناریوی پادکست حرفهای تبدیل میکند.</div></div>
|
|
|
|
| 356 |
</div>
|
| 357 |
</div>
|
| 358 |
<div style="text-align: center; margin-top: 1.5rem;">
|
|
@@ -401,7 +409,7 @@
|
|
| 401 |
|
| 402 |
<section class="landing-section features">
|
| 403 |
<h2>ویژگیهای کلیدی Ai Sada</h2>
|
| 404 |
-
<p class="subtitle">ابزارهایی قدرتمند برای خلق تجربههای صوتی بینظیر
|
| 405 |
<div class="features-grid">
|
| 406 |
<div class="feature-card"><div class="icon">🔊</div><h3>کیفیت صدای استودیویی</h3><p>صداهای تولید شده با هوش مصنوعی پیشرفته ما، کاملا طبیعی، شفاف و بدون نویز هستند.</p></div>
|
| 407 |
<div class="feature-card"><div class="icon">🎭</div><h3>۳۰ گوینده متنوع</h3><p>از میان گالری وسیعی از صداهای مرد و زن با احساسات و لحنهای مختلف، گوینده دلخواه خود را انتخاب کنید.</p></div>
|
|
@@ -409,15 +417,6 @@
|
|
| 409 |
</div>
|
| 410 |
</section>
|
| 411 |
|
| 412 |
-
<section class="contact-section">
|
| 413 |
-
<h2>پشتیبانی و ارتباط با ما</h2>
|
| 414 |
-
<p>برای سوالات، پیشنهادات یا دریافت پشتیبانی، از طریق تلگرام با ما در ارتباط باشید.</p>
|
| 415 |
-
<a href="https://t.me/ezmarynoori" target="_blank" rel="noopener noreferrer" class="contact-button">
|
| 416 |
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M9.78 18.65l.28-4.23 7.68-6.92c.34-.31-.07-.46-.52-.19L7.74 13.3 3.64 12c-.88-.25-.89-.86.2-1.3l15.97-6.16c.73-.33 1.43.18 1.15 1.3l-2.72 12.58c-.28 1.13-1.04 1.4-1.74.88L14.25 16l-2.47 2.35c-.22.24-.42.44-.69.44l.32-4.14z"></path></svg>
|
| 417 |
-
<span>ارتباط در تلگرام</span>
|
| 418 |
-
</a>
|
| 419 |
-
</section>
|
| 420 |
-
|
| 421 |
<footer class="site-footer"><p>© 1404 - تمام حقوق برای <a href="#">Ai Sada</a> محفوظ است.</p></footer>
|
| 422 |
</div>
|
| 423 |
|
|
@@ -473,7 +472,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 473 |
{ id: "Enceladus", name: "کامیار (مرد)", gender: "male", desc: "مصمم و جدی", imgUrl: "https://uploadkon.ir/uploads/127805_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۴۱۴.jpg", samples:["https://uploadkon.ir/uploads/566606_26کامیار-یک-2-.mp3"], sampleTexts: [""] },
|
| 474 |
{ id: "Iapetus", name: "کیانوش (مرد)", gender: "male", desc: "درخشان و گیرا", imgUrl: "https://uploadkon.ir/uploads/c98b05_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۶۰۵.jpg", samples:["https://uploadkon.ir/uploads/63fd06_26کیانوش-یک-2-.mp3"], sampleTexts: [""] },
|
| 475 |
{ id: "Puck", name: "پویا (مرد)", gender: "male", desc: "بازیگوش و سرزنده", imgUrl: "https://uploadkon.ir/uploads/ca3605_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۸۳۹.jpg", samples:["https://uploadkon.ir/uploads/7d1306_26پویا-یک-2-.mp3"], sampleTexts: [""] },
|
| 476 |
-
{ id: "Kore", name: "مهتاب (زن)", gender: "female", desc: "نجواگر و آرامشبخش", imgUrl: "https://uploadkon.ir/uploads/b66605_25IMG-۲۰۲۵۰۷۰۵-۱۲۳
|
| 477 |
{ id: "Fenrir", name: "سام (مرد)", gender: "male", desc: "جسور و بیباک", imgUrl: "https://uploadkon.ir/uploads/03c005_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۴۱۳.jpg", samples:["https://uploadkon.ir/uploads/467f06_26سام-یک-2-.mp3"], sampleTexts: [""] },
|
| 478 |
{ id: "Leda", name: "لیدا (زن)", gender: "female", desc: "کلاسیک و باوقار", imgUrl: "https://uploadkon.ir/uploads/710305_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۷۳۱.jpg", samples:["https://uploadkon.ir/uploads/547606_26لیدا-یک-2-.mp3"], sampleTexts: [""] }
|
| 479 |
];
|
|
@@ -687,6 +686,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 687 |
<div class="ai-podcast-title">ساخت پادکست هوشمند</div>
|
| 688 |
<div class="ai-podcast-subtitle">تبدیل موضوع یا مقاله به گفتگوی حرفهای</div>
|
| 689 |
</div>
|
|
|
|
| 690 |
`;
|
| 691 |
podcastCard.addEventListener('click', () => {
|
| 692 |
hideModal(speakerModal);
|
|
@@ -752,10 +752,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 752 |
studioContainer.className = 'podcast-studio-container';
|
| 753 |
studioContainer.innerHTML = `
|
| 754 |
<div class="form-group"><label>🎤 تیم گویندگان</label><div id="project-speakers-container"><div id="add-speaker-card" class="speaker-display-card"><div class="plus-icon">+</div><h3>افزودن گوینده</h3></div></div></div>
|
| 755 |
-
<div class="form-group">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 756 |
<button type="submit" id="generate-btn-podcast" class="generate-btn"><span class="btn-text">🎙️ ساخت فایل صوتی نهایی</span><div class="spinner"></div></button>
|
| 757 |
-
<div id="podcast-output-section" class="output-section" style="margin-top: 2rem; border-style: solid; background-color: transparent;">
|
| 758 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
`;
|
| 760 |
container.appendChild(studioContainer);
|
| 761 |
|
|
@@ -813,15 +822,30 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 813 |
turnDiv.className = 'script-turn';
|
| 814 |
const turnIndex = container.children.length;
|
| 815 |
turnDiv.innerHTML = `
|
| 816 |
-
<div class="turn-speaker-selector">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 817 |
<div class="turn-content">
|
| 818 |
<textarea>${text || ''}</textarea>
|
| 819 |
<div class="turn-player-container">
|
| 820 |
-
<button type="button" class="turn-play-btn">
|
|
|
|
|
|
|
|
|
|
| 821 |
<audio class="turn-audio" style="display:none;"></audio>
|
| 822 |
-
<div class="loading-state-wrapper" style="display:none;">
|
| 823 |
-
|
| 824 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
</div>
|
| 826 |
</div>
|
| 827 |
<button type="button" class="remove-turn-btn" title="حذف نوبت">×</button>`;
|
|
@@ -931,7 +955,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 931 |
hideModal(longTextModal);
|
| 932 |
await clearProjectState();
|
| 933 |
activePodcastSpeakers =[];
|
| 934 |
-
podcastMasterAudioBlobs =
|
| 935 |
|
| 936 |
const chunks = splitText(text);
|
| 937 |
let scriptData =[];
|
|
@@ -954,7 +978,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 954 |
}
|
| 955 |
|
| 956 |
async function asyncPool(poolLimit, array, iteratorFn) {
|
| 957 |
-
const ret =
|
| 958 |
for (const item of array) {
|
| 959 |
const p = Promise.resolve().then(() => iteratorFn(item));
|
| 960 |
ret.push(p);
|
|
@@ -1107,8 +1131,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1107 |
const retryBtn = playerContainer.querySelector('.turn-retry-btn');
|
| 1108 |
const loadingWrapper = playerContainer.querySelector('.loading-state-wrapper');
|
| 1109 |
const successMsg = playerContainer.querySelector('.replace-success-message');
|
|
|
|
| 1110 |
|
| 1111 |
-
retryBtn.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1112 |
try {
|
| 1113 |
const response = await fetch(`${API_BASE}/generate`, {
|
| 1114 |
method: 'POST',
|
|
@@ -1156,11 +1185,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1156 |
await saveAudioSegment(turnIndex, finalBlob);
|
| 1157 |
setupTurnPlayer(turnDiv, turnIndex);
|
| 1158 |
|
| 1159 |
-
loadingWrapper.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
| 1160 |
setTimeout(async () => {
|
| 1161 |
successMsg.style.display = 'none';
|
| 1162 |
-
if (podcastMasterAudioBlobs.every(b => b))
|
| 1163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1164 |
}, 2500);
|
| 1165 |
} catch (error) {
|
| 1166 |
alert("خطا در جایگزینی قطعه.");
|
|
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>تولید صدای هوشمند و پادکست | AI Sada</title>
|
| 7 |
+
<meta name="description" content="با AI Sada، متن فارسی خود را به صدایی طبیعی و با کیفیت استودیویی تبدیل کنید.">
|
| 8 |
<!-- Font Awesome for Icons -->
|
| 9 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 10 |
|
|
|
|
| 23 |
--radius-card: 24px; --radius-btn: 14px; --radius-input: 12px;
|
| 24 |
--transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
| 25 |
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
| 26 |
--card-bg: #FFFFFF;
|
|
|
|
| 27 |
}
|
| 28 |
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
|
| 29 |
@keyframes fadeInOut { 0% { opacity: 0; transform: translateY(10px); } 10% { opacity: 1; transform: translateY(0); } 90% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-10px); } }
|
|
|
|
| 37 |
@keyframes pulse-soft { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
|
| 38 |
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateY(0);} 40% {transform: translateY(-15px);} 60% {transform: translateY(-7px);} }
|
| 39 |
|
| 40 |
+
/* PODCAST STUDIO VIEW EXACTLY AS ORIGINAL */
|
| 41 |
.podcast-studio-container { width: 100%; animation: fadeIn 0.5s ease-out; }
|
| 42 |
.podcast-studio-container .form-group-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; }
|
| 43 |
.podcast-studio-container #project-speakers-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1.5rem; text-align: center; }
|
|
|
|
| 94 |
.podcast-studio-container .loading-state-wrapper .loading-text { font-size: 0.85em; font-weight: 600; color: var(--accent-primary); white-space: nowrap; transition: all 0.3s; }
|
| 95 |
.podcast-studio-container .turn-retry-btn { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; background: none; border: 1px solid var(--input-border); color: var(--text-secondary); padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); cursor: pointer; transition: var(--transition-smooth); margin-right: auto; display: flex; align-items: center; gap: 6px; }
|
| 96 |
.podcast-studio-container .turn-retry-btn:hover { border-color: var(--accent-secondary); color: var(--accent-secondary); background: var(--accent-secondary-glow); }
|
| 97 |
+
.podcast-studio-container .turn-retry-btn svg { width: 16px; height: 16px; fill: currentColor; }
|
| 98 |
.podcast-studio-container .replace-success-message, .podcast-studio-container .main-update-message { font-family: var(--app-font); font-size: 0.85em; font-weight: 600; padding: 0.4rem 0.8rem; border-radius: var(--radius-btn); white-space: nowrap; display: none; align-items: center; justify-content: center; margin-right: auto; }
|
| 99 |
.podcast-studio-container .replace-success-message { background-color: var(--accent-secondary-glow); color: var(--accent-secondary); }
|
| 100 |
.podcast-studio-container .main-update-message { background: linear-gradient(90deg, #e6fffa, #b2f5ea); color: #2c7a7b; border: 1px solid #81e6d9; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
| 101 |
|
| 102 |
.beautiful-download-btn { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; max-width: 400px; margin: 1.5rem auto 0.5rem auto; padding: 16px 24px; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); color: #fff; border: none; border-radius: 14px; font-family: var(--app-font); font-weight: 800; font-size: 1.1em; cursor: pointer; position: relative; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(37, 99, 235, 0.1), 0 2px 4px -1px rgba(37, 99, 235, 0.06); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-bottom: 4px solid #1d4ed8; }
|
| 103 |
.beautiful-download-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 10px 15px -3px rgba(37, 99, 235, 0.2), 0 4px 6px -2px rgba(37, 99, 235, 0.1); filter: brightness(1.05); }
|
| 104 |
+
.beautiful-download-btn:active:not(:disabled) { transform: translateY(2px); border-bottom-width: 0px; margin-top: calc(1.5rem + 4px); }
|
| 105 |
|
| 106 |
#clear-history-btn-podcast { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; max-width: 300px; margin: 1rem auto 0 auto; padding: 12px; background-color: #fff; border: 2px dashed #cbd5e0; color: #718096; font-family: var(--app-font); font-size: 0.95em; font-weight: 700; border-radius: 12px; cursor: pointer; transition: all 0.3s ease; }
|
| 107 |
#clear-history-btn-podcast:hover { border-color: #fc8181; color: #e53e3e; background-color: #fff5f5; transform: translateY(-2px); }
|
| 108 |
+
#clear-history-btn-podcast svg { width: 18px; height: 18px; fill: currentColor; }
|
| 109 |
+
|
| 110 |
+
@media (max-width: 768px) { .podcast-studio-container .script-turn { flex-direction: column; } .podcast-studio-container .remove-turn-btn { align-self: flex-end; margin-top: -1.5rem; margin-right: -0.5rem; } .podcast-studio-container .turn-speaker-selector { width: 100%; } }
|
| 111 |
+
|
| 112 |
/* Modals and Options */
|
| 113 |
#long-text-modal .modal-dialog { max-width: 600px; background: linear-gradient(160deg, #ffffff 0%, #f7f9fc 100%); padding: 3rem 2.5rem; max-height: 90vh; overflow-y: auto; }
|
| 114 |
#long-text-modal .modal-header { border: none; justify-content: center; flex-direction: column; text-align: center; margin-bottom: 1.5rem; }
|
|
|
|
| 122 |
.option-content { flex-grow: 1; text-align: right; }
|
| 123 |
.option-title { font-weight: 800; font-size: 1rem; color: var(--text-primary); margin-bottom: 2px; }
|
| 124 |
.option-subtitle { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.4; }
|
| 125 |
+
.option-arrow { color: var(--text-tertiary); transition: transform 0.3s ease; font-size: 1.2rem; } .option-card:hover .option-arrow { color: var(--accent-primary); transform: translateX(-5px); }
|
| 126 |
.special-ai-card { background: linear-gradient(135deg, #f3f4f6 0%, #ffffff 100%); border-color: #d1d5db; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
|
| 127 |
.special-ai-card:hover { border-color: #8b5cf6; background: linear-gradient(135deg, #f5f3ff 0%, #ffffff 100%); box-shadow: 0 10px 20px rgba(139, 92, 246, 0.15); }
|
| 128 |
.special-ai-card .option-icon { background: #ede9fe; color: #8b5cf6; }
|
|
|
|
| 136 |
.confirm-modal-dialog p { color: var(--text-secondary); margin: 0 0 2rem 0; line-height: 1.7; }
|
| 137 |
.confirm-modal-actions { display: flex; gap: 1rem; justify-content: center; }
|
| 138 |
.confirm-btn, .cancel-btn { font-family: var(--app-font); font-size: 1em; font-weight: 700; padding: 0.8rem 1.5rem; border-radius: var(--radius-btn); border: none; cursor: pointer; transition: all 0.2s ease; flex-grow: 1; }
|
| 139 |
+
.confirm-btn { background-color: #e53e3e; color: white; box-shadow: 0 4px 10px -2px rgba(229, 62, 62, 0.4); }
|
| 140 |
+
.confirm-btn:hover { background-color: #c53030; transform: translateY(-2px); box-shadow: 0 6px 14px -3px rgba(229, 62, 62, 0.5); }
|
| 141 |
+
.cancel-btn { background-color: var(--accent-primary); color: white; box-shadow: 0 4px 10px -2px rgba(74, 108, 250, 0.4); }
|
| 142 |
+
.cancel-btn:hover { background-color: var(--accent-primary-hover); transform: translateY(-2px); box-shadow: 0 6px 14px -3px rgba(74, 108, 250, 0.5); }
|
| 143 |
|
| 144 |
.ai-podcast-card { grid-column: 1 / -1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 18px !important; display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: space-between !important; padding: 0 1.5rem !important; margin-top: 10px; cursor: pointer; min-height: 90px; }
|
| 145 |
.ai-podcast-card:hover { transform: translateY(-5px) scale(1.02) !important; box-shadow: 0 15px 30px rgba(118, 75, 162, 0.6) !important; }
|
|
|
|
| 147 |
.ai-podcast-title { font-size: 1.3rem; font-weight: 900; color: white; margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
|
| 148 |
.ai-podcast-subtitle { font-size: 0.9rem; color: rgba(255,255,255,0.9); font-weight: 500; }
|
| 149 |
.ai-podcast-icon { font-size: 2.5rem; margin-left: 1rem; animation: pulse-soft 2s infinite; }
|
| 150 |
+
.ai-arrow-icon { width: 30px; height: 30px; background: rgba(255,255,255,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; transition: 0.3s; }
|
| 151 |
+
.ai-podcast-card:hover .ai-arrow-icon { background: white; color: #764ba2; transform: translateX(-5px); }
|
| 152 |
|
| 153 |
#ai-podcast-modal .modal-header h2 { background: linear-gradient(90deg, #FF0080, #7928CA); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.8rem; }
|
| 154 |
#ai-podcast-input { width: 100%; min-height: 180px; padding: 1.2rem; border-radius: 16px; border: 2px solid var(--input-border); background-color: var(--input-bg); font-family: var(--app-font); font-size: 1rem; line-height: 1.8; resize: vertical; transition: all 0.3s; margin: 1.5rem 0; }
|
| 155 |
+
#ai-podcast-input:focus { outline: none; border-color: #7928CA; background-color: white; box-shadow: 0 0 0 4px rgba(121, 40, 202, 0.1); }
|
| 156 |
|
| 157 |
.sample-player-container { position: absolute; top: 50%; left: 4px; transform: translateY(-50%); display: flex; flex-direction: column; gap: 4px; z-index: 6; }
|
| 158 |
.sample-play-btn { width: 24px; height: 24px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; background: linear-gradient(145deg, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.25)); border: 1px solid rgba(255, 255, 255, 0.15); }
|
|
|
|
| 162 |
.sample-play-btn.playing { background: var(--accent-secondary); }
|
| 163 |
.sample-play-btn.playing .play-icon-svg { display: none; }
|
| 164 |
.sample-play-btn.playing .playing-indicator { display: flex; }
|
|
|
|
| 165 |
|
| 166 |
#sample-text-display { position: fixed; bottom: 25px; left: 50%; width: 90%; max-width: 600px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 20px; padding: 20px 24px; box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15); z-index: 2000; text-align: right; direction: rtl; display: flex; flex-direction: column; align-items: flex-start; opacity: 0; visibility: hidden; transform: translateX(-50%) translateY(30px) scale(0.95); transition: all 0.5s; }
|
| 167 |
#sample-text-display.active { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0) scale(1); }
|
| 168 |
+
#sample-text-display h4 { margin: 0 0 10px; color: var(--accent-primary); font-size: 0.8em; font-weight: 800; display: flex; align-items: center; gap: 8px; }
|
| 169 |
+
#sample-text-display h4::before { content: '\f10d'; font-family: 'Font Awesome 6 Free'; font-weight: 900; font-size: 1.2em; opacity: 0.7; }
|
| 170 |
#sample-text-display p { margin: 0; color: var(--text-primary); font-weight: 500; font-size: 0.9em; line-height: 1.7; }
|
| 171 |
|
| 172 |
#speaker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 1.5rem; padding-bottom: 0; transition: padding-bottom 0.4s; }
|
|
|
|
| 257 |
.feature-card h3 { font-size: 1.3em; margin-bottom: 0.5rem; color: var(--text-primary); }
|
| 258 |
.site-footer { text-align: center; padding: 2rem 0; margin-top: 3rem; border-top: 1px solid var(--panel-border); color: var(--text-tertiary); }
|
| 259 |
.site-footer a { color: var(--accent-primary); text-decoration: none; font-weight: 600; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
</style>
|
| 261 |
</head>
|
| 262 |
<body>
|
|
|
|
| 266 |
<div id="standard-view">
|
| 267 |
<header class="app-header">
|
| 268 |
<h1>مولد صدای هوشمند Ai Sada</h1>
|
| 269 |
+
<p>کیفیت استودیو، قدرت هوش مصنوعی. متن فارسی را به صدایی طبیعی و با کیفیت استودیویی تبدیل کنید یا پادکستهای چند نفره بسازید.</p>
|
| 270 |
</header>
|
| 271 |
<main class="main-content">
|
| 272 |
<form id="standard-tts-form" onsubmit="return false;">
|
|
|
|
| 345 |
<div class="option-card" data-mode="single">
|
| 346 |
<div class="option-icon">👤</div>
|
| 347 |
<div class="option-content"><div class="option-title">با یک گوینده</div><div class="option-subtitle">متن به صورت یکپارچه با گوینده انتخابی شما خوانده میشود.</div></div>
|
| 348 |
+
<div class="option-arrow">❮</div>
|
| 349 |
</div>
|
| 350 |
<div class="option-card" data-mode="double">
|
| 351 |
<div class="option-icon">👥</div>
|
| 352 |
<div class="option-content"><div class="option-title">با دو گوینده</div><div class="option-subtitle">متن به صورت هوشمند بین دو گوینده تصادفی تقسیم میشود.</div></div>
|
| 353 |
+
<div class="option-arrow">❮</div>
|
| 354 |
</div>
|
| 355 |
<div class="option-card" data-mode="multi">
|
| 356 |
<div class="option-icon">🎭</div>
|
| 357 |
<div class="option-content"><div class="option-title">با چندین گوینده</div><div class="option-subtitle">هوش مصنوعی متن را تحلیل کرده و با چندین گوینده تصادفی اجرا میکند.</div></div>
|
| 358 |
+
<div class="option-arrow">❮</div>
|
| 359 |
</div>
|
| 360 |
<div class="option-card special-ai-card" data-mode="ai-podcast">
|
| 361 |
<div class="option-icon">🎙️</div>
|
| 362 |
<div class="option-content"><div class="option-title">تبدیل متن به پادکست</div><div class="option-subtitle">هوش مصنوعی متن شما را به سناریوی پادکست حرفهای تبدیل میکند.</div></div>
|
| 363 |
+
<div class="option-arrow">✨</div>
|
| 364 |
</div>
|
| 365 |
</div>
|
| 366 |
<div style="text-align: center; margin-top: 1.5rem;">
|
|
|
|
| 409 |
|
| 410 |
<section class="landing-section features">
|
| 411 |
<h2>ویژگیهای کلیدی Ai Sada</h2>
|
| 412 |
+
<p class="subtitle">ابزارهایی قدرتمند برای خلق تجربههای صوتی بینظیر.</p>
|
| 413 |
<div class="features-grid">
|
| 414 |
<div class="feature-card"><div class="icon">🔊</div><h3>کیفیت صدای استودیویی</h3><p>صداهای تولید شده با هوش مصنوعی پیشرفته ما، کاملا طبیعی، شفاف و بدون نویز هستند.</p></div>
|
| 415 |
<div class="feature-card"><div class="icon">🎭</div><h3>۳۰ گوینده متنوع</h3><p>از میان گالری وسیعی از صداهای مرد و زن با احساسات و لحنهای مختلف، گوینده دلخواه خود را انتخاب کنید.</p></div>
|
|
|
|
| 417 |
</div>
|
| 418 |
</section>
|
| 419 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
<footer class="site-footer"><p>© 1404 - تمام حقوق برای <a href="#">Ai Sada</a> محفوظ است.</p></footer>
|
| 421 |
</div>
|
| 422 |
|
|
|
|
| 472 |
{ id: "Enceladus", name: "کامیار (مرد)", gender: "male", desc: "مصمم و جدی", imgUrl: "https://uploadkon.ir/uploads/127805_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۴۱۴.jpg", samples:["https://uploadkon.ir/uploads/566606_26کامیار-یک-2-.mp3"], sampleTexts: [""] },
|
| 473 |
{ id: "Iapetus", name: "کیانوش (مرد)", gender: "male", desc: "درخشان و گیرا", imgUrl: "https://uploadkon.ir/uploads/c98b05_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۶۰۵.jpg", samples:["https://uploadkon.ir/uploads/63fd06_26کیانوش-یک-2-.mp3"], sampleTexts: [""] },
|
| 474 |
{ id: "Puck", name: "پویا (مرد)", gender: "male", desc: "بازیگوش و سرزنده", imgUrl: "https://uploadkon.ir/uploads/ca3605_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۸۳۹.jpg", samples:["https://uploadkon.ir/uploads/7d1306_26پویا-یک-2-.mp3"], sampleTexts: [""] },
|
| 475 |
+
{ id: "Kore", name: "مهتاب (زن)", gender: "female", desc: "نجواگر و آرامشبخش", imgUrl: "https://uploadkon.ir/uploads/b66605_25IMG-۲۰۲۵۰۷۰۵-۱۲۳0۳۵.jpg", samples:["https://uploadkon.ir/uploads/9bfc06_26مهتاب-یک-2-.mp3"], sampleTexts: [""] },
|
| 476 |
{ id: "Fenrir", name: "سام (مرد)", gender: "male", desc: "جسور و بیباک", imgUrl: "https://uploadkon.ir/uploads/03c005_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۴۱۳.jpg", samples:["https://uploadkon.ir/uploads/467f06_26سام-یک-2-.mp3"], sampleTexts: [""] },
|
| 477 |
{ id: "Leda", name: "لیدا (زن)", gender: "female", desc: "کلاسیک و باوقار", imgUrl: "https://uploadkon.ir/uploads/710305_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۷۳۱.jpg", samples:["https://uploadkon.ir/uploads/547606_26لیدا-یک-2-.mp3"], sampleTexts: [""] }
|
| 478 |
];
|
|
|
|
| 686 |
<div class="ai-podcast-title">ساخت پادکست هوشمند</div>
|
| 687 |
<div class="ai-podcast-subtitle">تبدیل موضوع یا مقاله به گفتگوی حرفهای</div>
|
| 688 |
</div>
|
| 689 |
+
<div class="ai-arrow-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg></div>
|
| 690 |
`;
|
| 691 |
podcastCard.addEventListener('click', () => {
|
| 692 |
hideModal(speakerModal);
|
|
|
|
| 752 |
studioContainer.className = 'podcast-studio-container';
|
| 753 |
studioContainer.innerHTML = `
|
| 754 |
<div class="form-group"><label>🎤 تیم گویندگان</label><div id="project-speakers-container"><div id="add-speaker-card" class="speaker-display-card"><div class="plus-icon">+</div><h3>افزودن گوینده</h3></div></div></div>
|
| 755 |
+
<div class="form-group">
|
| 756 |
+
<label>📜 سناریوی گفتگو</label>
|
| 757 |
+
<div id="podcast-script-container"></div>
|
| 758 |
+
<button type="button" id="add-turn-btn">+ افزودن نوبت گفتگو</button>
|
| 759 |
+
</div>
|
| 760 |
<button type="submit" id="generate-btn-podcast" class="generate-btn"><span class="btn-text">🎙️ ساخت فایل صوتی نهایی</span><div class="spinner"></div></button>
|
| 761 |
+
<div id="podcast-output-section" class="output-section" style="margin-top: 2rem; border-style: solid; background-color: transparent;">
|
| 762 |
+
<div class="status-message">فایل نهایی در اینجا ظاهر خواهد شد.</div>
|
| 763 |
+
</div>
|
| 764 |
+
<button type="button" id="clear-history-btn-podcast">
|
| 765 |
+
<svg viewBox="0 0 24 24"><path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path></svg>
|
| 766 |
+
حذف کامل پروژه و شروع مجدد
|
| 767 |
+
</button>
|
| 768 |
`;
|
| 769 |
container.appendChild(studioContainer);
|
| 770 |
|
|
|
|
| 822 |
turnDiv.className = 'script-turn';
|
| 823 |
const turnIndex = container.children.length;
|
| 824 |
turnDiv.innerHTML = `
|
| 825 |
+
<div class="turn-speaker-selector">
|
| 826 |
+
<div class="custom-select-container" data-selected-id="${speakerId}">
|
| 827 |
+
<div class="custom-select-trigger"></div>
|
| 828 |
+
<div class="custom-select-options"></div>
|
| 829 |
+
</div>
|
| 830 |
+
</div>
|
| 831 |
<div class="turn-content">
|
| 832 |
<textarea>${text || ''}</textarea>
|
| 833 |
<div class="turn-player-container">
|
| 834 |
+
<button type="button" class="turn-play-btn">
|
| 835 |
+
<svg viewBox="0 0 24 24" class="play-icon"><path d="M8 5v14l11-7z"></path></svg>
|
| 836 |
+
<svg viewBox="0 0 24 24" class="pause-icon"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></svg>
|
| 837 |
+
</button>
|
| 838 |
<audio class="turn-audio" style="display:none;"></audio>
|
| 839 |
+
<div class="loading-state-wrapper" style="display:none;">
|
| 840 |
+
<div class="spinner"></div>
|
| 841 |
+
<span class="loading-text">در حال جایگزینی...</span>
|
| 842 |
+
</div>
|
| 843 |
+
<button type="button" class="turn-retry-btn">
|
| 844 |
+
<svg viewBox="0 0 24 24"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></svg>
|
| 845 |
+
جایگزین قطعه
|
| 846 |
+
</button>
|
| 847 |
+
<span class="replace-success-message">فایل جدید جایگزین شد</span>
|
| 848 |
+
<span class="main-update-message">فایل اصلی بروز شد</span>
|
| 849 |
</div>
|
| 850 |
</div>
|
| 851 |
<button type="button" class="remove-turn-btn" title="حذف نوبت">×</button>`;
|
|
|
|
| 955 |
hideModal(longTextModal);
|
| 956 |
await clearProjectState();
|
| 957 |
activePodcastSpeakers =[];
|
| 958 |
+
podcastMasterAudioBlobs =[];
|
| 959 |
|
| 960 |
const chunks = splitText(text);
|
| 961 |
let scriptData =[];
|
|
|
|
| 978 |
}
|
| 979 |
|
| 980 |
async function asyncPool(poolLimit, array, iteratorFn) {
|
| 981 |
+
const ret =[]; const executing =[];
|
| 982 |
for (const item of array) {
|
| 983 |
const p = Promise.resolve().then(() => iteratorFn(item));
|
| 984 |
ret.push(p);
|
|
|
|
| 1131 |
const retryBtn = playerContainer.querySelector('.turn-retry-btn');
|
| 1132 |
const loadingWrapper = playerContainer.querySelector('.loading-state-wrapper');
|
| 1133 |
const successMsg = playerContainer.querySelector('.replace-success-message');
|
| 1134 |
+
const mainMsg = playerContainer.querySelector('.main-update-message');
|
| 1135 |
|
| 1136 |
+
retryBtn.style.display = 'none';
|
| 1137 |
+
loadingWrapper.style.display = 'flex';
|
| 1138 |
+
successMsg.style.display = 'none';
|
| 1139 |
+
mainMsg.style.display = 'none';
|
| 1140 |
+
|
| 1141 |
try {
|
| 1142 |
const response = await fetch(`${API_BASE}/generate`, {
|
| 1143 |
method: 'POST',
|
|
|
|
| 1185 |
await saveAudioSegment(turnIndex, finalBlob);
|
| 1186 |
setupTurnPlayer(turnDiv, turnIndex);
|
| 1187 |
|
| 1188 |
+
loadingWrapper.style.display = 'none';
|
| 1189 |
+
successMsg.style.display = 'flex';
|
| 1190 |
+
successMsg.style.animation = 'fadeInOut 2.5s forwards';
|
| 1191 |
+
|
| 1192 |
setTimeout(async () => {
|
| 1193 |
successMsg.style.display = 'none';
|
| 1194 |
+
if (podcastMasterAudioBlobs.every(b => b)) {
|
| 1195 |
+
mainMsg.style.display = 'flex';
|
| 1196 |
+
mainMsg.style.animation = 'fadeInOut 2.5s forwards';
|
| 1197 |
+
await rebuildFinalAudio(false);
|
| 1198 |
+
}
|
| 1199 |
+
setTimeout(() => { mainMsg.style.display = 'none'; retryBtn.style.display = 'flex'; }, 2500);
|
| 1200 |
}, 2500);
|
| 1201 |
} catch (error) {
|
| 1202 |
alert("خطا در جایگزینی قطعه.");
|