Opera8 commited on
Commit
d811ffc
·
verified ·
1 Parent(s): 39152f8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +106 -65
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>رابط عمومی سخنگو - OmniVoice API</title>
7
- <!-- استفاده از Tailwind CSS برای طراحی زیبا و مدرن -->
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;700;900&display=swap" rel="stylesheet">
10
  <style>
@@ -28,7 +28,7 @@
28
  <!-- متن ورودی -->
29
  <div>
30
  <label class="block text-sm font-bold text-slate-300 mb-2">متن مورد نظر برای تبدیل به گفتار:</label>
31
- <textarea id="inputText" rows="4" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-teal-500" placeholder="متن خود را اینجا بنویسید... (پشتیبانی از زبان‌های مختلف)"></textarea>
32
  </div>
33
 
34
  <!-- انتخاب حالت -->
@@ -127,20 +127,19 @@
127
  <div class="bg-slate-900/30 border border-slate-700 rounded-xl p-4">
128
  <label class="block text-xs font-bold text-slate-300 mb-1">توکن Hugging Face (اختیاری):</label>
129
  <input type="password" id="hfToken" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-2 text-xs text-white focus:outline-none focus:ring-1 focus:ring-teal-500" placeholder="Bearer hf_...">
130
- <p class="text-[10px] text-slate-500 mt-1">در صورت سنگین بودن ترافیک سرور یا داشتن اکانت اختصاصی، وارد کردن توکن الزامی است.</p>
131
  </div>
132
 
133
  <!-- بخش خروجی و لاگ و دکمه‌ها -->
134
  <div class="space-y-4">
135
- <button id="btnGenerate" onclick="generateSpeech()" class="w-full bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-600 hover:to-emerald-600 text-white font-bold py-3 px-6 rounded-xl transition duration-300 shadow-lg transform hover:-translate-y-0.5 active:translate-y-0 flex items-center justify-center space-x-2 space-x-reverse">
136
  <span id="btnText">تولید صدا (Synthesize)</span>
137
  </button>
138
 
139
- <!-- لاگ وضعیت و پیشرفت -->
140
- <div id="statusBox" class="hidden bg-slate-900 p-3 rounded-lg border border-slate-800 text-xs space-y-1 max-h-24 overflow-y-auto">
141
  <div class="flex items-center space-x-2 space-x-reverse text-slate-400">
142
- <span class="animate-pulse block w-2 h-2 rounded-full bg-amber-500"></span>
143
- <span id="statusMessage">در حال اتصال به API...</span>
144
  </div>
145
  </div>
146
 
@@ -157,11 +156,10 @@
157
  </div>
158
  </div>
159
 
160
- <!-- بارگذاری کلاینت رسمی گرادیو از CDN به عنوان ماژول مروگر -->
161
  <script type="module">
162
  import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
163
 
164
- // مدیریت تغییر ظاهر فیلدها متناسب با حالت کاری منتخب
165
  window.toggleModeFields = function() {
166
  const selectedMode = document.querySelector('input[name="mode"]:checked').value;
167
  const cloneFields = document.getElementById("cloneFields");
@@ -176,12 +174,12 @@
176
  }
177
  }
178
 
179
- // عملکرد اصلی دکمه ساخت صدا
180
  window.generateSpeech = async function() {
181
  const btn = document.getElementById("btnGenerate");
182
  const btnText = document.getElementById("btnText");
183
  const statusBox = document.getElementById("statusBox");
184
  const statusMessage = document.getElementById("statusMessage");
 
185
  const resultContainer = document.getElementById("resultContainer");
186
  const audioPlayer = document.getElementById("audioPlayer");
187
  const btnDownload = document.getElementById("btnDownload");
@@ -200,84 +198,127 @@
200
  const hfToken = document.getElementById("hfToken").value.trim();
201
 
202
  if (!text) {
203
- alert("لطفاً متن مورد نظر خود را وارد کنید.");
204
  return;
205
  }
206
 
207
- // غیرفعال کردن دکمه و نمایش وضعیت پردازش
208
  btn.disabled = true;
209
- btnText.innerText = "در حال پردازش...";
210
  statusBox.classList.remove("hidden");
211
  resultContainer.classList.add("hidden");
212
- statusMessage.innerText = "در حال ایجاد اتصال به فضای OmniVoice...";
 
213
 
214
  try {
215
- // اتصال به مدل Hugging Face
216
  const app = await Client.connect("k2-fsa/OmniVoice", {
217
  token: hfToken ? `Bearer ${hfToken}` : undefined
218
  });
219
 
220
- // پردازش فایل صوتی در حالت Clone
221
- let refAudioPayload = null;
222
- let refTextVal = null;
223
  if (mode === "clone") {
 
 
 
 
 
 
 
224
  const refAudioFile = document.getElementById("refAudio").files[0];
225
  if (!refAudioFile) {
226
- throw new Error("برای شبیه‌سازی صدا، لطفاً یک فایل صوتی آپلود کنید.");
227
  }
228
- statusMessage.innerText = "در حال آپلود فایل صوتی مرجع...";
229
- refAudioPayload = handle_file(refAudioFile);
230
- refTextVal = document.getElementById("refText").value.trim() || null;
231
- }
232
 
233
- statusMessage.innerText = ر حال ارسال درخواست به صف پردازش مدل...";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
- // تشخیص خودکار اندپوینت API بر اساس تعداد آرگومان‌های ورودی تابع اصلی مدل
236
- let endpointIndex = "/predict";
237
- try {
238
- const mainDep = app.config.dependencies.find(dep => dep.inputs && dep.inputs.length === 13);
239
- if (mainDep) {
240
- endpointIndex = mainDep.api_name ? `/${mainDep.api_name}` : mainDep.id;
241
  }
242
- } catch(e) {
243
- console.warn("خطا در سیستم خودیاب پویای اندپوینت، استفاده از حالت پیش‌فرض:", e);
244
  }
245
 
246
- // ارسال اطلاعات به صف و انتظار برای نتیجه
247
- const result = await app.predict(endpointIndex, [
248
- text, // 1. Text
249
- language, // 2. Language
250
- refAudioPayload, // 3. Reference Audio
251
- instruct, // 4. Instruct
252
- steps, // 5. Steps (num_step)
253
- guidance, // 6. Guidance scale
254
- denoise, // 7. Denoise
255
- speed, // 8. Speed
256
- duration, // 9. Duration
257
- preprocess, // 10. Preprocess prompt
258
- postprocess, // 11. Postprocess output
259
- mode, // 12. Mode ("clone" or "design")
260
- refTextVal // 13. Reference text (ref_text)
261
- ]);
262
 
263
- // بررسی صحت داده‌های دریافتی
264
- if (result && result.data && result.data[0]) {
265
- const audioUrl = result.data[0].url;
266
-
267
- statusMessage.innerText = "پایان پردازش! صدا با موفقیت ساخته شد.";
268
- audioPlayer.src = audioUrl;
269
- btnDownload.href = audioUrl;
270
-
271
- resultContainer.classList.remove("hidden");
272
- } else {
273
- throw new Error(result.data[1] || "خطایی از سمت سرور رخ داد اما خطای دقیقی گزارش نشد.");
274
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  } catch (error) {
277
- console.error(error);
278
- statusMessage.innerText = `خطا: ${error.message || error}`;
279
- } finally {
280
- // فعال‌سازی مجدد دکمه
281
  btn.disabled = false;
282
  btnText.innerText = "تولید صدا (Synthesize)";
283
  }
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>رابط عمومی سخنگو - OmniVoice API</title>
7
+ <!-- استفاده از Tailwind CSS برای طراحی زیبا -->
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;700;900&display=swap" rel="stylesheet">
10
  <style>
 
28
  <!-- متن ورودی -->
29
  <div>
30
  <label class="block text-sm font-bold text-slate-300 mb-2">متن مورد نظر برای تبدیل به گفتار:</label>
31
+ <textarea id="inputText" rows="4" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-teal-500" placeholder="متن خود را اینجا بنویسید..."></textarea>
32
  </div>
33
 
34
  <!-- انتخاب حالت -->
 
127
  <div class="bg-slate-900/30 border border-slate-700 rounded-xl p-4">
128
  <label class="block text-xs font-bold text-slate-300 mb-1">توکن Hugging Face (اختیاری):</label>
129
  <input type="password" id="hfToken" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-2 text-xs text-white focus:outline-none focus:ring-1 focus:ring-teal-500" placeholder="Bearer hf_...">
 
130
  </div>
131
 
132
  <!-- بخش خروجی و لاگ و دکمه‌ها -->
133
  <div class="space-y-4">
134
+ <button id="btnGenerate" onclick="generateSpeech()" class="w-full bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-600 hover:to-emerald-600 text-white font-bold py-3 px-6 rounded-xl transition duration-300 shadow-lg transform hover:-translate-y-0.5 active:translate-y-0 flex items-center justify-center">
135
  <span id="btnText">تولید صدا (Synthesize)</span>
136
  </button>
137
 
138
+ <!-- لاگ وضعیت زنده صف -->
139
+ <div id="statusBox" class="hidden bg-slate-900 p-3 rounded-lg border border-slate-800 text-xs">
140
  <div class="flex items-center space-x-2 space-x-reverse text-slate-400">
141
+ <span class="animate-pulse block w-2.5 h-2.5 rounded-full bg-amber-500" id="statusDot"></span>
142
+ <span id="statusMessage">در حال اتصال...</span>
143
  </div>
144
  </div>
145
 
 
156
  </div>
157
  </div>
158
 
159
+ <!-- بارگذاری کلاینت رسمی گرادیو از CDN -->
160
  <script type="module">
161
  import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
162
 
 
163
  window.toggleModeFields = function() {
164
  const selectedMode = document.querySelector('input[name="mode"]:checked').value;
165
  const cloneFields = document.getElementById("cloneFields");
 
174
  }
175
  }
176
 
 
177
  window.generateSpeech = async function() {
178
  const btn = document.getElementById("btnGenerate");
179
  const btnText = document.getElementById("btnText");
180
  const statusBox = document.getElementById("statusBox");
181
  const statusMessage = document.getElementById("statusMessage");
182
+ const statusDot = document.getElementById("statusDot");
183
  const resultContainer = document.getElementById("resultContainer");
184
  const audioPlayer = document.getElementById("audioPlayer");
185
  const btnDownload = document.getElementById("btnDownload");
 
198
  const hfToken = document.getElementById("hfToken").value.trim();
199
 
200
  if (!text) {
201
+ alert("لطفاً ابتدا متن مورد نظر خود را وارد کنید.");
202
  return;
203
  }
204
 
 
205
  btn.disabled = true;
206
+ btnText.innerText = "منتظر بمانید...";
207
  statusBox.classList.remove("hidden");
208
  resultContainer.classList.add("hidden");
209
+ statusDot.className = "animate-pulse block w-2.5 h-2.5 rounded-full bg-amber-500";
210
+ statusMessage.innerText = "در حال اتصال به سرور هوش مصنوعی...";
211
 
212
  try {
213
+ // اتصال به فضای کاری گرادیو
214
  const app = await Client.connect("k2-fsa/OmniVoice", {
215
  token: hfToken ? `Bearer ${hfToken}` : undefined
216
  });
217
 
218
+ let endpoint = null;
219
+ let payload = [];
220
+
221
  if (mode === "clone") {
222
+ // برای حالت شبیه‌سازی صدا به دنبال اندپوینت با ۱۲ ورودی می‌گردد
223
+ const cloneDep = app.config.dependencies.find(dep => dep.inputs && dep.inputs.length === 12);
224
+ if (!cloneDep) {
225
+ throw new Error("اندپوینت متناسب با شبیه‌سازی صدا در سرور یافت نشد.");
226
+ }
227
+ endpoint = cloneDep.api_name ? `/${cloneDep.api_name}` : cloneDep.id;
228
+
229
  const refAudioFile = document.getElementById("refAudio").files[0];
230
  if (!refAudioFile) {
231
+ throw new Error("برای شبیه‌سازی، آپلود یک فایل صوتی مرجع الزامی است.");
232
  }
233
+
234
+ statusMessage.innerText = "در حال آپلود و پردازش فایل صوتی مرجع...";
235
+ const refAudioPayload = handle_file(refAudioFile);
236
+ const refTextVal = document.getElementById("refText").value.trim() || null;
237
 
238
+ // ۱۲ پارامتر ساختار فرعی شبیه‌سازی صدا
239
+ payload = [
240
+ text,
241
+ language,
242
+ refAudioPayload,
243
+ refTextVal,
244
+ instruct,
245
+ steps,
246
+ guidance,
247
+ denoise,
248
+ speed,
249
+ duration,
250
+ preprocess,
251
+ postprocess
252
+ ];
253
+ } else {
254
+ // برای طراحی صدا به دنبال اندپوینت با ورودی‌های بیشتر می‌گردد
255
+ const designDep = app.config.dependencies.find(dep => dep.inputs && dep.inputs.length > 12);
256
+ if (!designDep) {
257
+ throw new Error("اندپوینت متناسب با طراحی صدا یافت نشد.");
258
+ }
259
+ endpoint = designDep.api_name ? `/${designDep.api_name}` : designDep.id;
260
+ const expectedLength = designDep.inputs.length;
261
+
262
+ // ساختار پایه‌ای تابع طراحی صدا در دمو
263
+ payload = [
264
+ text,
265
+ language,
266
+ steps,
267
+ guidance,
268
+ denoise,
269
+ speed,
270
+ duration,
271
+ preprocess,
272
+ postprocess
273
+ ];
274
 
275
+ // مابقی پارامترها مربوط به دراپ‌داون‌های دمو هستند که با "Auto" مقداردهی می‌شوند
276
+ while (payload.length < expectedLength) {
277
+ payload.push("Auto");
 
 
 
278
  }
 
 
279
  }
280
 
281
+ statusMessage.innerText = "در حال ورود به صف پردازش مدل...";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
+ // استفاده از submit به جای predict جهت مانیتورینگ زنده موقعیت در صف پردازش کارت گرافیک
284
+ const job = app.submit(endpoint, payload);
285
+
286
+ job.on("status", (status) => {
287
+ if (status.stage === "pending") {
288
+ const queuePos = status.position !== undefined ? status.position : "در حال تخمین";
289
+ const eta = status.eta !== undefined ? `${Math.round(status.eta)} ثانیه` : "نامشخص";
290
+ statusMessage.innerText = `در صف انتظار سیستم. موقعیت شما در صف: ${queuePos} | زمان تخمینی: ${eta}`;
291
+ } else if (status.stage === "processing") {
292
+ statusDot.className = "animate-pulse block w-2.5 h-2.5 rounded-full bg-teal-500";
293
+ statusMessage.innerText = "نوبت شما فرا رسید! کارت گرافیک (ZeroGPU) در حال تولید صدا است...";
294
+ }
295
+ });
296
+
297
+ job.on("data", (event) => {
298
+ const result = event.data;
299
+ if (result && result[0]) {
300
+ const audioUrl = result[0].url;
301
+ audioPlayer.src = audioUrl;
302
+ btnDownload.href = audioUrl;
303
+
304
+ statusDot.className = "block w-2.5 h-2.5 rounded-full bg-emerald-500";
305
+ statusMessage.innerText = "صدا با موفقیت ایجاد شد!";
306
+ resultContainer.classList.remove("hidden");
307
+ } else {
308
+ throw new Error("داده‌ای از سمت سرور بازگردانده نشد.");
309
+ }
310
+ });
311
+
312
+ job.on("error", (err) => {
313
+ statusDot.className = "block w-2.5 h-2.5 rounded-full bg-red-500";
314
+ statusMessage.innerText = `خطای پردازش: ${err.message || err}`;
315
+ btn.disabled = false;
316
+ btnText.innerText = "تولید صدا (Synthesize)";
317
+ });
318
 
319
  } catch (error) {
320
+ statusDot.className = "block w-2.5 h-2.5 rounded-full bg-red-500";
321
+ statusMessage.innerText = `خطا در برقراری ارتباط: ${error.message || error}`;
 
 
322
  btn.disabled = false;
323
  btnText.innerText = "تولید صدا (Synthesize)";
324
  }