Ezmary commited on
Commit
20cf1e7
·
verified ·
1 Parent(s): ddd04af

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +44 -56
templates/index.html CHANGED
@@ -350,9 +350,8 @@
350
  import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
351
 
352
  (function() {
353
- let isProcessing = false;
354
-
355
- let userSubscriptionStatus = 'free';
356
  let userFingerprint = null;
357
  let countdownInterval = null;
358
  const PREMIUM_PAGE_ID = '1149636';
@@ -390,7 +389,7 @@
390
  subscriptionBadge.className = 'paid-badge';
391
  creditInfoDiv.style.display = 'none';
392
  upgradeBtn.style.display = 'none';
393
- if (!isProcessing) genBtn.disabled = false;
394
  if(countdownInterval) clearInterval(countdownInterval);
395
  } else {
396
  subscriptionBadge.textContent = 'نسخه رایگان';
@@ -420,7 +419,7 @@
420
  upgradeBtn.style.display = 'block';
421
  startCountdown(result.reset_timestamp);
422
  } else {
423
- if (!isProcessing) genBtn.disabled = false;
424
  upgradeBtn.style.display = 'none';
425
  if(countdownInterval) clearInterval(countdownInterval);
426
  creditInfoDiv.textContent = `شما ${result.credits_remaining} اعتبار ساخت ویدیوی رایگان در این هفته دارید.`;
@@ -470,7 +469,10 @@
470
  document.getElementById('upgrade-button').addEventListener('click', () => {
471
  parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*');
472
  });
 
 
473
 
 
474
  const resultContainer = document.getElementById('result-container');
475
  const imageFileInput = document.getElementById('imageFile');
476
  const imagePreview = document.getElementById('imagePreview');
@@ -499,8 +501,10 @@
499
  const DEFAULT_PROMPT_TEXT = "";
500
  const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
501
  const DEFAULT_VIDEO_DURATION = 4.3;
502
- const GLOBAL_TIMEOUT = 150000;
503
- const QUEUE_TIMEOUT = 45000;
 
 
504
 
505
  let lastAttemptedVideoPayload = null;
506
 
@@ -524,7 +528,7 @@
524
 
525
  function showCriticalError(message, isTranslationError = false) {
526
  const lowerCaseMessage = (message || "").toLowerCase();
527
- if (lowerCaseMessage.includes("gpu") || lowerCaseMessage.includes("quota") || lowerCaseMessage.includes("exceeded") || lowerCaseMessage.includes("limit") || lowerCaseMessage.includes("capacity") || lowerCaseMessage.includes("queue full") || lowerCaseMessage.includes("server is busy")) {
528
  const detailedGpuErrorHtml = `<div class="gpu-error-message-container"><h2><span class="icon">💡</span> راهنمای رفع محدودیت ویدیو</h2><div class="error-content"><strong class="error-message-title">ارور محدودیت استفاده از GPU</strong><p>مدل‌های هوش مصنوعی برای اجرا به کارت‌های گرافیک بسیار قدرتمند نیاز دارند که این امر گاهی محدودیت‌هایی جی پی یو را در استفاده از آن‌ها ایجاد می‌کند. با این حال، خبر خوب این است که این محدودیت‌ها با استفاده از یک ترفند ساده قابل رفع هستند.</p><p><strong>برای رفع محدودیت هر بار اینگونه عمل کنید:</strong></p><ul><li>اگر از اینترنت سیم‌کارت استفاده می‌کنید، فیلترشکن خود را حتما خاموش کنید.</li><li>به مدت ۱۰ تا ۱۵ ثانیه، گوشی خود را در حالت هواپیما قرار دهید و سپس آن را غیرفعال کنید. این محدودیت با همین روش برداشته میشود</li><li>اگر از وای فای استفاده می‌کنید، مودم خود را یک بار خاموش و روشن کنید (بهتر است برای استفاده نامحدود از برنامه از اینترنت سیم کارت استفاده کنید).</li></ul><p>خلاصه: هربار این صفحه اومد اینگونه عمل کنید از اینترنت سیم کارت استف��ده کنید، فیلتر شکن خاموش باشه، یک یا دوبار حالت هواپیما رو روشن و خاموش کنید. تلاش مجدداً بزنید این ارور بر طرف میشه، با این روش میشه بصورت نامحدود با این برنامه ویدیو ساخت☘️</p></div><div class="gpu-error-actions"><button class="action-button back-button" id="gpuErrorGoBackBtn">بازگشت</button><button class="action-button retry-button" id="gpuErrorRetryBtn">تلاش مجدد</button></div></div>`;
529
  criticalErrorSection.innerHTML = detailedGpuErrorHtml;
530
  document.getElementById('gpuErrorGoBackBtn').addEventListener('click', hideCriticalError);
@@ -539,6 +543,7 @@
539
  statusSection.classList.remove('active');
540
  outputSection.classList.remove('active');
541
  aiLoader.style.display = 'none';
 
542
  }
543
 
544
  function hideCriticalError() {
@@ -585,7 +590,6 @@
585
  imagePreview.src = TRANSPARENT_PIXEL_SRC;
586
  imageDropZone.classList.remove('has-image');
587
  promptInput.value = DEFAULT_PROMPT_TEXT;
588
- isProcessing = false;
589
  generateButton.disabled = false;
590
  durationSlider.value = DEFAULT_VIDEO_DURATION;
591
  durationValueDisplay.textContent = `${parseFloat(DEFAULT_VIDEO_DURATION).toFixed(1)} ثانیه`;
@@ -632,7 +636,9 @@
632
  headers: {'Content-Type': 'application/json'},
633
  body: JSON.stringify({ fingerprint: userFingerprint })
634
  });
635
- if (!creditResponse.ok) console.warn("Credit deduction failed AFTER video generation.");
 
 
636
  checkFreeUserCredit();
637
  } catch (creditError) {
638
  console.error("Critical error deducting credit:", creditError);
@@ -649,6 +655,7 @@
649
  finalSeedElement.textContent = `Seed: ${usedSeed}`;
650
  prepareCustomDownloadLink(videoUrl);
651
  }, 300);
 
652
  }
653
 
654
  function prepareCustomDownloadLink(videoUrl) {
@@ -702,8 +709,7 @@
702
 
703
  if (!persianPrompt) { addStatusMessage('لطفاً توضیحات را وارد کنید.', 'error'); return; }
704
  if (!imageFile) { addStatusMessage('لطفاً تصویر انتخاب کنید.', 'error'); return; }
705
-
706
- isProcessing = true;
707
  clearStatusMessages();
708
  hideCriticalError();
709
  generateButton.disabled = true;
@@ -715,68 +721,50 @@
715
  addStatusMessage('در حال آماده سازی...', 'info');
716
 
717
  lastAttemptedVideoPayload = { persianPrompt, imageSourceFile: imageFile, duration };
718
-
719
  try {
720
  const englishPrompt = await translatePersianToEnglish(persianPrompt);
721
  addStatusMessage(`ترجمه: ${englishPrompt}`, 'info');
 
722
 
723
  const client = await Client.connect(VIDEO_SPACE_NAME);
724
- const job = client.submit(0, [
725
- imageFile, englishPrompt, 6, DEFAULT_NEGATIVE_PROMPT,
726
- duration, 1, 1, 0, true
727
- ]);
728
-
729
- const videoGenerationPromise = new Promise(async (resolve, reject) => {
730
- let hasStartedProcessing = false;
731
- const startTime = Date.now();
732
-
733
- while (true) {
734
- const status = await job.status();
735
-
736
- if (status.stage === "error") {
737
- return reject(new Error(status.message || "خطای نامشخص در سرور"));
738
- }
739
-
740
- if (status.stage === "completed") {
741
- return resolve(status);
742
- }
743
-
744
- if (status.stage === "pending" || status.stage === "queued") {
745
- setAiLoaderText(`در صف پردازش... (نفر ${status.queue_position || '?'})`);
746
- } else if (status.stage === "generating" || status.stage === "iterating") {
747
- if (!hasStartedProcessing) hasStartedProcessing = true;
748
- setAiLoaderText("در حال ساخت فریم‌های ویدیو...");
749
- }
750
 
751
- if (!hasStartedProcessing && Date.now() - startTime > QUEUE_TIMEOUT) {
752
- return reject(new Error("Server is busy. لطفا مجددا تلاش کنید. (Queue Timeout)"));
753
- }
754
-
755
- if (Date.now() - startTime > GLOBAL_TIMEOUT) {
756
- return reject(new Error('پردازش بیش از حد طولانی شد. (Global Timeout)'));
757
- }
758
-
759
- await new Promise(r => setTimeout(r, 2000)); // 2 ثانیه صبر
760
- }
761
  });
762
 
763
- const result = await videoGenerationPromise;
 
 
 
 
 
 
 
 
 
 
 
764
 
 
 
 
 
 
765
  await handleSuccessfulVideoGeneration(result);
766
 
767
  } catch (errorCaught) {
768
  const isTranslationErr = errorCaught.message.toLowerCase().includes("مترجم");
769
  showCriticalError(`خطا: ${errorCaught.message}`, isTranslationErr);
770
- } finally {
771
- isProcessing = false;
772
- if (!document.getElementById('upgrade-button').style.display || document.getElementById('upgrade-button').style.display === 'none') {
773
- generateButton.disabled = false;
774
- }
775
  }
776
  };
777
 
778
  generateButton.addEventListener('click', async () => {
779
- if (isProcessing) return;
780
 
781
  if (userSubscriptionStatus === 'paid') {
782
  await proceedWithVideoGeneration();
 
350
  import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
351
 
352
  (function() {
353
+ // === START: بخش مدیریت اعتبار و ارتباط با والد ===
354
+ let userSubscriptionStatus = 'free'; // 'free' or 'paid'
 
355
  let userFingerprint = null;
356
  let countdownInterval = null;
357
  const PREMIUM_PAGE_ID = '1149636';
 
389
  subscriptionBadge.className = 'paid-badge';
390
  creditInfoDiv.style.display = 'none';
391
  upgradeBtn.style.display = 'none';
392
+ genBtn.disabled = false;
393
  if(countdownInterval) clearInterval(countdownInterval);
394
  } else {
395
  subscriptionBadge.textContent = 'نسخه رایگان';
 
419
  upgradeBtn.style.display = 'block';
420
  startCountdown(result.reset_timestamp);
421
  } else {
422
+ genBtn.disabled = false;
423
  upgradeBtn.style.display = 'none';
424
  if(countdownInterval) clearInterval(countdownInterval);
425
  creditInfoDiv.textContent = `شما ${result.credits_remaining} اعتبار ساخت ویدیوی رایگان در این هفته دارید.`;
 
469
  document.getElementById('upgrade-button').addEventListener('click', () => {
470
  parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*');
471
  });
472
+ // === END: بخش مدیریت اعتبار ===
473
+
474
 
475
+ // DOM Elements
476
  const resultContainer = document.getElementById('result-container');
477
  const imageFileInput = document.getElementById('imageFile');
478
  const imagePreview = document.getElementById('imagePreview');
 
501
  const DEFAULT_PROMPT_TEXT = "";
502
  const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
503
  const DEFAULT_VIDEO_DURATION = 4.3;
504
+ // --- START: TIMEOUT LOGIC ---
505
+ // زمان وقفه به میلی‌ثانیه: 2.5 دقیقه
506
+ const GENERATION_TIMEOUT = 150000;
507
+ // --- END: TIMEOUT LOGIC ---
508
 
509
  let lastAttemptedVideoPayload = null;
510
 
 
528
 
529
  function showCriticalError(message, isTranslationError = false) {
530
  const lowerCaseMessage = (message || "").toLowerCase();
531
+ if (lowerCaseMessage.includes("gpu") || lowerCaseMessage.includes("quota") || lowerCaseMessage.includes("exceeded") || lowerCaseMessage.includes("limit") || lowerCaseMessage.includes("capacity") || lowerCaseMessage.includes("queue full")) {
532
  const detailedGpuErrorHtml = `<div class="gpu-error-message-container"><h2><span class="icon">💡</span> راهنمای رفع محدودیت ویدیو</h2><div class="error-content"><strong class="error-message-title">ارور محدودیت استفاده از GPU</strong><p>مدل‌های هوش مصنوعی برای اجرا به کارت‌های گرافیک بسیار قدرتمند نیاز دارند که این امر گاهی محدودیت‌هایی جی پی یو را در استفاده از آن‌ها ایجاد می‌کند. با این حال، خبر خوب این است که این محدودیت‌ها با استفاده از یک ترفند ساده قابل رفع هستند.</p><p><strong>برای رفع محدودیت هر بار اینگونه عمل کنید:</strong></p><ul><li>اگر از اینترنت سیم‌کارت استفاده می‌کنید، فیلترشکن خود را حتما خاموش کنید.</li><li>به مدت ۱۰ تا ۱۵ ثانیه، گوشی خود را در حالت هواپیما قرار دهید و سپس آن را غیرفعال کنید. این محدودیت با همین روش برداشته میشود</li><li>اگر از وای فای استفاده می‌کنید، مودم خود را یک بار خاموش و روشن کنید (بهتر است برای استفاده نامحدود از برنامه از اینترنت سیم کارت استفاده کنید).</li></ul><p>خلاصه: هربار این صفحه اومد اینگونه عمل کنید از اینترنت سیم کارت استف��ده کنید، فیلتر شکن خاموش باشه، یک یا دوبار حالت هواپیما رو روشن و خاموش کنید. تلاش مجدداً بزنید این ارور بر طرف میشه، با این روش میشه بصورت نامحدود با این برنامه ویدیو ساخت☘️</p></div><div class="gpu-error-actions"><button class="action-button back-button" id="gpuErrorGoBackBtn">بازگشت</button><button class="action-button retry-button" id="gpuErrorRetryBtn">تلاش مجدد</button></div></div>`;
533
  criticalErrorSection.innerHTML = detailedGpuErrorHtml;
534
  document.getElementById('gpuErrorGoBackBtn').addEventListener('click', hideCriticalError);
 
543
  statusSection.classList.remove('active');
544
  outputSection.classList.remove('active');
545
  aiLoader.style.display = 'none';
546
+ generateButton.disabled = false;
547
  }
548
 
549
  function hideCriticalError() {
 
590
  imagePreview.src = TRANSPARENT_PIXEL_SRC;
591
  imageDropZone.classList.remove('has-image');
592
  promptInput.value = DEFAULT_PROMPT_TEXT;
 
593
  generateButton.disabled = false;
594
  durationSlider.value = DEFAULT_VIDEO_DURATION;
595
  durationValueDisplay.textContent = `${parseFloat(DEFAULT_VIDEO_DURATION).toFixed(1)} ثانیه`;
 
636
  headers: {'Content-Type': 'application/json'},
637
  body: JSON.stringify({ fingerprint: userFingerprint })
638
  });
639
+ if (!creditResponse.ok) {
640
+ console.warn("Credit deduction failed AFTER video generation.");
641
+ }
642
  checkFreeUserCredit();
643
  } catch (creditError) {
644
  console.error("Critical error deducting credit:", creditError);
 
655
  finalSeedElement.textContent = `Seed: ${usedSeed}`;
656
  prepareCustomDownloadLink(videoUrl);
657
  }, 300);
658
+ generateButton.disabled = false;
659
  }
660
 
661
  function prepareCustomDownloadLink(videoUrl) {
 
709
 
710
  if (!persianPrompt) { addStatusMessage('لطفاً توضیحات را وارد کنید.', 'error'); return; }
711
  if (!imageFile) { addStatusMessage('لطفاً تصویر انتخاب کنید.', 'error'); return; }
712
+
 
713
  clearStatusMessages();
714
  hideCriticalError();
715
  generateButton.disabled = true;
 
721
  addStatusMessage('در حال آماده سازی...', 'info');
722
 
723
  lastAttemptedVideoPayload = { persianPrompt, imageSourceFile: imageFile, duration };
724
+
725
  try {
726
  const englishPrompt = await translatePersianToEnglish(persianPrompt);
727
  addStatusMessage(`ترجمه: ${englishPrompt}`, 'info');
728
+ setAiLoaderText("در حال ساخت ویدیو...");
729
 
730
  const client = await Client.connect(VIDEO_SPACE_NAME);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
731
 
732
+ // --- START: TIMEOUT LOGIC ---
733
+ // ایجاد یک پرامیس که پس از زمان مشخص شده، با خطا رد می‌شود
734
+ const timeoutPromise = new Promise((_, reject) => {
735
+ setTimeout(() => {
736
+ reject(new Error('پردازش ویدیو بیش از 2.5 دقیقه طول کشید. لطفاً مجدداً تلاش کنید. ممکن است سرور شلوغ باشد.'));
737
+ }, GENERATION_TIMEOUT);
 
 
 
 
738
  });
739
 
740
+ // ایجاد پرامیس اصلی برای ساخت ویدیو
741
+ const videoGenerationPromise = client.predict(0, [
742
+ imageFile, // 0: input_image
743
+ englishPrompt, // 1: prompt
744
+ 6, // 2: steps
745
+ DEFAULT_NEGATIVE_PROMPT,// 3: negative_prompt
746
+ duration, // 4: duration_seconds
747
+ 1, // 5: guidance_scale
748
+ 1, // 6: guidance_scale_2
749
+ 0, // 7: seed (0 for random)
750
+ true // 8: randomize_seed
751
+ ]);
752
 
753
+ // رقابت بین پرامیس ویدیو و پرامیس تایم‌اوت
754
+ // هر کدام زودتر تمام شود، نتیجه آن برگردانده می‌شود
755
+ const result = await Promise.race([videoGenerationPromise, timeoutPromise]);
756
+ // --- END: TIMEOUT LOGIC ---
757
+
758
  await handleSuccessfulVideoGeneration(result);
759
 
760
  } catch (errorCaught) {
761
  const isTranslationErr = errorCaught.message.toLowerCase().includes("مترجم");
762
  showCriticalError(`خطا: ${errorCaught.message}`, isTranslationErr);
 
 
 
 
 
763
  }
764
  };
765
 
766
  generateButton.addEventListener('click', async () => {
767
+ if (generateButton.disabled) return;
768
 
769
  if (userSubscriptionStatus === 'paid') {
770
  await proceedWithVideoGeneration();