Update templates/index.html
Browse files- 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 503 |
-
|
|
|
|
|
|
|
| 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")
|
| 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)
|
|
|
|
|
|
|
| 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 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
}
|
| 758 |
-
|
| 759 |
-
await new Promise(r => setTimeout(r, 2000)); // 2 ثانیه صبر
|
| 760 |
-
}
|
| 761 |
});
|
| 762 |
|
| 763 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 (
|
| 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();
|