Update templates/index.html
Browse files- templates/index.html +71 -143
templates/index.html
CHANGED
|
@@ -88,90 +88,18 @@
|
|
| 88 |
textarea { width: 100%; padding: 1rem 1.2rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background-color: var(--input-bg); color: var(--text-primary); box-shadow: var(--shadow-sm) inset; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: var(--transition-smooth); min-height: 120px; resize: vertical; }
|
| 89 |
textarea:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-primary-glow), var(--shadow-sm) inset; background-color: var(--panel-bg); }
|
| 90 |
|
| 91 |
-
.slider-group {
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
}
|
| 95 |
-
.slider-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
color: var(--text-secondary);
|
| 104 |
-
font-size: 0.95rem;
|
| 105 |
-
}
|
| 106 |
-
.slider-value {
|
| 107 |
-
font-weight: 700;
|
| 108 |
-
font-size: 1rem;
|
| 109 |
-
color: var(--accent-primary);
|
| 110 |
-
background-color: var(--input-bg);
|
| 111 |
-
padding: 0.25rem 0.75rem;
|
| 112 |
-
border-radius: 8px;
|
| 113 |
-
border: 1px solid var(--input-border);
|
| 114 |
-
min-width: 80px;
|
| 115 |
-
text-align: center;
|
| 116 |
-
}
|
| 117 |
-
.slider-wrapper {
|
| 118 |
-
display: flex;
|
| 119 |
-
align-items: center;
|
| 120 |
-
gap: 1rem;
|
| 121 |
-
}
|
| 122 |
-
.slider-icon {
|
| 123 |
-
width: 22px;
|
| 124 |
-
height: 22px;
|
| 125 |
-
color: var(--text-tertiary);
|
| 126 |
-
flex-shrink: 0;
|
| 127 |
-
opacity: 0.8;
|
| 128 |
-
}
|
| 129 |
-
input[type="range"] {
|
| 130 |
-
-webkit-appearance: none;
|
| 131 |
-
appearance: none;
|
| 132 |
-
flex-grow: 1;
|
| 133 |
-
width: 100%;
|
| 134 |
-
height: 12px;
|
| 135 |
-
background: var(--input-bg);
|
| 136 |
-
border-radius: var(--radius-input);
|
| 137 |
-
outline: none;
|
| 138 |
-
cursor: pointer;
|
| 139 |
-
direction: rtl;
|
| 140 |
-
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
|
| 141 |
-
transition: var(--transition-smooth);
|
| 142 |
-
}
|
| 143 |
-
input[type="range"]::-webkit-slider-thumb {
|
| 144 |
-
-webkit-appearance: none;
|
| 145 |
-
appearance: none;
|
| 146 |
-
width: 24px;
|
| 147 |
-
height: 24px;
|
| 148 |
-
background: linear-gradient(145deg, var(--accent-primary), var(--accent-secondary));
|
| 149 |
-
border-radius: 50%;
|
| 150 |
-
border: 3px solid white;
|
| 151 |
-
box-shadow: var(--shadow-md);
|
| 152 |
-
transition: all 0.2s ease-in-out;
|
| 153 |
-
margin-top: -6px;
|
| 154 |
-
}
|
| 155 |
-
input[type="range"]::-moz-range-thumb {
|
| 156 |
-
width: 24px;
|
| 157 |
-
height: 24px;
|
| 158 |
-
background: linear-gradient(145deg, var(--accent-primary), var(--accent-secondary));
|
| 159 |
-
border-radius: 50%;
|
| 160 |
-
border: 3px solid white;
|
| 161 |
-
box-shadow: var(--shadow-md);
|
| 162 |
-
transition: all 0.2s ease-in-out;
|
| 163 |
-
}
|
| 164 |
-
input[type="range"]:hover {
|
| 165 |
-
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1), 0 0 0 4px var(--accent-primary-glow);
|
| 166 |
-
}
|
| 167 |
-
input[type="range"]:hover::-webkit-slider-thumb {
|
| 168 |
-
box-shadow: 0 0 0 10px var(--accent-primary-glow), var(--shadow-md);
|
| 169 |
-
transform: scale(1.1);
|
| 170 |
-
}
|
| 171 |
-
input[type="range"]:active::-webkit-slider-thumb {
|
| 172 |
-
transform: scale(1.2);
|
| 173 |
-
box-shadow: 0 0 0 12px var(--accent-primary-glow), var(--shadow-md);
|
| 174 |
-
}
|
| 175 |
|
| 176 |
#generateButton { display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem; font-size: 1.2rem; font-weight: 700; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease; box-shadow: 0 6px 12px -3px var(--accent-primary-glow), 0 6px 12px -3px rgba(15, 212, 168, 0.25); margin-top: 1rem; }
|
| 177 |
#generateButton svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); }
|
|
@@ -190,7 +118,8 @@
|
|
| 190 |
.generator-container { position: relative; width: 400px; max-width: 100%; height: 300px; border: 2px solid #38bdf8; border-radius: 20px; overflow: hidden; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); animation: pulse-loader 5s infinite cubic-bezier(0.4, 0, 0.6, 1); background-color: #161b22; color: #f0f6fc; }
|
| 191 |
@keyframes pulse-loader { 0% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } 50% { box-shadow: 0 0 60px rgba(56, 189, 248, 0.7); } 100% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } }
|
| 192 |
.noise-layer, .sketch-layer, .building-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
| 193 |
-
|
|
|
|
| 194 |
@keyframes fade-noise { 0% { opacity: 1; filter: blur(5px); } 30% { opacity: 0.8; filter: blur(2px); } 100% { opacity: 0; filter: blur(0px); } }
|
| 195 |
.sketch-layer { filter: grayscale(1) contrast(1.5) blur(3px); opacity: 0; animation: reveal-sketch 7s infinite ease-in-out; }
|
| 196 |
@keyframes reveal-sketch { 0% { opacity: 0; } 20% { opacity: 1; } 60% { opacity: 0.5; } 100% { opacity: 0; } }
|
|
@@ -220,12 +149,8 @@
|
|
| 220 |
.gpu-error-message-container .error-content ul { list-style-type: "▫️ "; padding-right: 20px; margin-top: 10px; }
|
| 221 |
.gpu-error-message-container .error-content li { margin-bottom: 6px; }
|
| 222 |
.gpu-error-actions { display: flex; gap: 15px; margin-top: 25px; }
|
| 223 |
-
.gpu-error-actions .action-button { padding: 12px 20px; border: none; border-radius: var(--igadlm-alpha-border-radius-md); font-size: 0.95em; font-weight: 600; cursor: pointer; font-family: inherit; width: 100%; box-shadow: var(--shadow-md); transition: var(--transition-smooth); text-transform: none; letter-spacing: normal; }
|
| 224 |
-
.gpu-error-actions .action-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
|
| 225 |
.gpu-error-actions .action-button.back-button { background-color: var(--panel-bg); color: var(--text-tertiary); border: 1px solid var(--panel-border); flex-grow: 0.45; }
|
| 226 |
-
.gpu-error-actions .action-button.back-button:hover:not(:disabled) { background-color: var(--input-bg); }
|
| 227 |
.gpu-error-actions .action-button.retry-button { background: linear-gradient(60deg, var(--igadlm-alpha-color-primary) 0%, var(--igadlm-alpha-color-secondary) 100%); color: white; flex-grow: 1; }
|
| 228 |
-
.gpu-error-actions .action-button.retry-button:hover:not(:disabled) { background: linear-gradient(60deg, var(--igadlm-alpha-color-primary-darker) 0%, var(--igadlm-alpha-color-secondary-darker) 100%); }
|
| 229 |
|
| 230 |
#subscription-status-badge { display: none; padding: 6px 16px; border-radius: 20px; font-size: 0.9em; font-weight: 700; margin-top: 1.25rem; letter-spacing: 0.5px; animation: badge-fade-in 0.6s 0.5s ease-out backwards; }
|
| 231 |
#subscription-status-badge.free-badge { background: linear-gradient(45deg, #6c757d, #495057); color: white; box-shadow: 0 4px 10px rgba(108, 117, 125, 0.3); }
|
|
@@ -493,18 +418,22 @@
|
|
| 493 |
const durationSlider = document.getElementById('durationSlider');
|
| 494 |
const durationValueDisplay = document.getElementById('durationValue');
|
| 495 |
|
|
|
|
| 496 |
const VIDEO_SPACE_NAME = "mrbui1990/wan2-2-fp8da-aoti-faster";
|
| 497 |
-
|
|
|
|
|
|
|
| 498 |
const FN_INDEX_TRANSLATE_SPEAK = 1;
|
| 499 |
const DEFAULT_TTS_VOICE_TRANSLATOR = "انگلیسی (آمریکا) - جنی (زن)";
|
|
|
|
| 500 |
const DEFAULT_NEGATIVE_PROMPT = "色调艳丽, 过曝, 静态, 细节模糊不清, 字幕, 风格, 作品, 画作, 画面, 静止, 整体发灰, 最差质量, 低质量, JPEG压缩残留, 丑陋的, 残缺的, 多余的手指, 画得不好的手部, 画得不好的脸部, 畸形的, 毁容的, 形态畸形的肢体, 手指融合, 静止不动的画面, 杂乱的背景, 三条腿, 背景人很多, 倒着走";
|
| 501 |
const DEFAULT_PROMPT_TEXT = "";
|
| 502 |
const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
| 503 |
const DEFAULT_VIDEO_DURATION = 4.3;
|
| 504 |
-
|
| 505 |
-
//
|
| 506 |
-
const GENERATION_TIMEOUT = 150000;
|
| 507 |
-
//
|
| 508 |
|
| 509 |
let lastAttemptedVideoPayload = null;
|
| 510 |
|
|
@@ -596,34 +525,34 @@
|
|
| 596 |
updateSliderStyle(durationSlider);
|
| 597 |
updateUIForSubscriptionStatus(userSubscriptionStatus);
|
| 598 |
}
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
|
|
|
| 603 |
if (aiLoader.style.display !== 'none') setAiLoaderText("در حال ترجمه دستور شما...");
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
} catch (error) { throw error; }
|
| 622 |
}
|
| 623 |
|
| 624 |
async function handleSuccessfulVideoGeneration(result) {
|
| 625 |
aiLoader.style.display = 'none';
|
| 626 |
-
if (!result.data?.[0]?.video?.url) {
|
| 627 |
showCriticalError('پاسخ دریافتی از سرور ویدیو معتبر نیست.');
|
| 628 |
return;
|
| 629 |
}
|
|
@@ -722,44 +651,43 @@
|
|
| 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 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
setTimeout(() => {
|
| 736 |
-
reject(new Error('پردازش ویدیو بیش از 2.5 دقیقه طول کشید. لطفاً مجدداً تلاش کنید. ممکن است سرور شلوغ باشد.'));
|
| 737 |
-
}, GENERATION_TIMEOUT);
|
| 738 |
-
});
|
| 739 |
|
| 740 |
-
// ایجاد پرامیس اصلی برای ساخت ویدیو
|
| 741 |
const videoGenerationPromise = client.predict(0, [
|
| 742 |
-
imageFile,
|
| 743 |
-
|
| 744 |
-
6,
|
| 745 |
-
DEFAULT_NEGATIVE_PROMPT,// 3: negative_prompt
|
| 746 |
-
duration,
|
| 747 |
-
1,
|
| 748 |
-
1,
|
| 749 |
-
0,
|
| 750 |
-
true
|
| 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 |
-
|
| 762 |
-
|
|
|
|
| 763 |
}
|
| 764 |
};
|
| 765 |
|
|
|
|
| 88 |
textarea { width: 100%; padding: 1rem 1.2rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background-color: var(--input-bg); color: var(--text-primary); box-shadow: var(--shadow-sm) inset; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: var(--transition-smooth); min-height: 120px; resize: vertical; }
|
| 89 |
textarea:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-primary-glow), var(--shadow-sm) inset; background-color: var(--panel-bg); }
|
| 90 |
|
| 91 |
+
.slider-group { margin-top: 2rem; margin-bottom: 1.5rem; }
|
| 92 |
+
.slider-label-container { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
|
| 93 |
+
.slider-label { font-weight: 600; color: var(--text-secondary); font-size: 0.95rem; }
|
| 94 |
+
.slider-value { font-weight: 700; font-size: 1rem; color: var(--accent-primary); background-color: var(--input-bg); padding: 0.25rem 0.75rem; border-radius: 8px; border: 1px solid var(--input-border); min-width: 80px; text-align: center; }
|
| 95 |
+
.slider-wrapper { display: flex; align-items: center; gap: 1rem; }
|
| 96 |
+
.slider-icon { width: 22px; height: 22px; color: var(--text-tertiary); flex-shrink: 0; opacity: 0.8; }
|
| 97 |
+
input[type="range"] { -webkit-appearance: none; appearance: none; flex-grow: 1; width: 100%; height: 12px; background: var(--input-bg); border-radius: var(--radius-input); outline: none; cursor: pointer; direction: rtl; box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); transition: var(--transition-smooth); }
|
| 98 |
+
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; background: linear-gradient(145deg, var(--accent-primary), var(--accent-secondary)); border-radius: 50%; border: 3px solid white; box-shadow: var(--shadow-md); transition: all 0.2s ease-in-out; margin-top: -6px; }
|
| 99 |
+
input[type="range"]::-moz-range-thumb { width: 24px; height: 24px; background: linear-gradient(145deg, var(--accent-primary), var(--accent-secondary)); border-radius: 50%; border: 3px solid white; box-shadow: var(--shadow-md); transition: all 0.2s ease-in-out; }
|
| 100 |
+
input[type="range"]:hover { box-shadow: inset 0 1px 3px rgba(0,0,0,0.1), 0 0 0 4px var(--accent-primary-glow); }
|
| 101 |
+
input[type="range"]:hover::-webkit-slider-thumb { box-shadow: 0 0 0 10px var(--accent-primary-glow), var(--shadow-md); transform: scale(1.1); }
|
| 102 |
+
input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.2); box-shadow: 0 0 0 12px var(--accent-primary-glow), var(--shadow-md); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
#generateButton { display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem; font-size: 1.2rem; font-weight: 700; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease; box-shadow: 0 6px 12px -3px var(--accent-primary-glow), 0 6px 12px -3px rgba(15, 212, 168, 0.25); margin-top: 1rem; }
|
| 105 |
#generateButton svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); }
|
|
|
|
| 118 |
.generator-container { position: relative; width: 400px; max-width: 100%; height: 300px; border: 2px solid #38bdf8; border-radius: 20px; overflow: hidden; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); animation: pulse-loader 5s infinite cubic-bezier(0.4, 0, 0.6, 1); background-color: #161b22; color: #f0f6fc; }
|
| 119 |
@keyframes pulse-loader { 0% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } 50% { box-shadow: 0 0 60px rgba(56, 189, 248, 0.7); } 100% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } }
|
| 120 |
.noise-layer, .sketch-layer, .building-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
| 121 |
+
/* FIX xmlns در SVG اینجا اعمال شده */
|
| 122 |
+
.noise-layer { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><rect width="100" height="100" fill="none"/><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.5" numOctaves="4" stitchTiles="stitch"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.6"/></svg>') repeat; opacity: 1; animation: fade-noise 7s infinite ease-in-out; }
|
| 123 |
@keyframes fade-noise { 0% { opacity: 1; filter: blur(5px); } 30% { opacity: 0.8; filter: blur(2px); } 100% { opacity: 0; filter: blur(0px); } }
|
| 124 |
.sketch-layer { filter: grayscale(1) contrast(1.5) blur(3px); opacity: 0; animation: reveal-sketch 7s infinite ease-in-out; }
|
| 125 |
@keyframes reveal-sketch { 0% { opacity: 0; } 20% { opacity: 1; } 60% { opacity: 0.5; } 100% { opacity: 0; } }
|
|
|
|
| 149 |
.gpu-error-message-container .error-content ul { list-style-type: "▫️ "; padding-right: 20px; margin-top: 10px; }
|
| 150 |
.gpu-error-message-container .error-content li { margin-bottom: 6px; }
|
| 151 |
.gpu-error-actions { display: flex; gap: 15px; margin-top: 25px; }
|
|
|
|
|
|
|
| 152 |
.gpu-error-actions .action-button.back-button { background-color: var(--panel-bg); color: var(--text-tertiary); border: 1px solid var(--panel-border); flex-grow: 0.45; }
|
|
|
|
| 153 |
.gpu-error-actions .action-button.retry-button { background: linear-gradient(60deg, var(--igadlm-alpha-color-primary) 0%, var(--igadlm-alpha-color-secondary) 100%); color: white; flex-grow: 1; }
|
|
|
|
| 154 |
|
| 155 |
#subscription-status-badge { display: none; padding: 6px 16px; border-radius: 20px; font-size: 0.9em; font-weight: 700; margin-top: 1.25rem; letter-spacing: 0.5px; animation: badge-fade-in 0.6s 0.5s ease-out backwards; }
|
| 156 |
#subscription-status-badge.free-badge { background: linear-gradient(45deg, #6c757d, #495057); color: white; box-shadow: 0 4px 10px rgba(108, 117, 125, 0.3); }
|
|
|
|
| 418 |
const durationSlider = document.getElementById('durationSlider');
|
| 419 |
const durationValueDisplay = document.getElementById('durationValue');
|
| 420 |
|
| 421 |
+
// ====== تنظیمات مدلها ======
|
| 422 |
const VIDEO_SPACE_NAME = "mrbui1990/wan2-2-fp8da-aoti-faster";
|
| 423 |
+
|
| 424 |
+
// مترجم: بهجای SSE از Gradio Client استفاده میکنیم (پایدار روی موبایل)
|
| 425 |
+
const TRANSLATOR_SPACE_NAME = "hamed744/translate-tts-aloha";
|
| 426 |
const FN_INDEX_TRANSLATE_SPEAK = 1;
|
| 427 |
const DEFAULT_TTS_VOICE_TRANSLATOR = "انگلیسی (آمریکا) - جنی (زن)";
|
| 428 |
+
|
| 429 |
const DEFAULT_NEGATIVE_PROMPT = "色调艳丽, 过曝, 静态, 细节模糊不清, 字幕, 风格, 作品, 画作, 画面, 静止, 整体发灰, 最差质量, 低质量, JPEG压缩残留, 丑陋的, 残缺的, 多余的手指, 画得不好的手部, 画得不好的脸部, 畸形的, 毁容的, 形态畸形的肢体, 手指融合, 静止不动的画面, 杂乱的背景, 三条腿, 背景人很多, 倒着走";
|
| 430 |
const DEFAULT_PROMPT_TEXT = "";
|
| 431 |
const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
| 432 |
const DEFAULT_VIDEO_DURATION = 4.3;
|
| 433 |
+
|
| 434 |
+
// --- TIMEOUTS ---
|
| 435 |
+
const GENERATION_TIMEOUT = 150000; // 2.5 دقیقه برای ساخت ویدیو
|
| 436 |
+
const TRANSLATION_TIMEOUT = 20000; // 20 ثانیه برای ترجمه
|
| 437 |
|
| 438 |
let lastAttemptedVideoPayload = null;
|
| 439 |
|
|
|
|
| 525 |
updateSliderStyle(durationSlider);
|
| 526 |
updateUIForSubscriptionStatus(userSubscriptionStatus);
|
| 527 |
}
|
| 528 |
+
|
| 529 |
+
// ==== نسخه جدید مترجم با Gradio Client + تایماوت ====
|
| 530 |
+
async function translatePersianToEnglish(persianText, { timeoutMs = TRANSLATION_TIMEOUT } = {}) {
|
| 531 |
+
if (!persianText?.trim()) throw new Error("متن ترجمه خالی است.");
|
| 532 |
+
|
| 533 |
if (aiLoader.style.display !== 'none') setAiLoaderText("در حال ترجمه دستور شما...");
|
| 534 |
+
const translator = await Client.connect(TRANSLATOR_SPACE_NAME);
|
| 535 |
+
|
| 536 |
+
const predictPromise = translator.predict(FN_INDEX_TRANSLATE_SPEAK, [
|
| 537 |
+
persianText,
|
| 538 |
+
DEFAULT_TTS_VOICE_TRANSLATOR,
|
| 539 |
+
0, 0, 0
|
| 540 |
+
]);
|
| 541 |
+
|
| 542 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 543 |
+
setTimeout(() => reject(new Error("مهلت ترجمه به پایان رسید.")), timeoutMs)
|
| 544 |
+
);
|
| 545 |
+
|
| 546 |
+
const resp = await Promise.race([predictPromise, timeoutPromise]);
|
| 547 |
+
|
| 548 |
+
const english = resp?.data?.[0];
|
| 549 |
+
if (!english) throw new Error("ترجمه نامعتبر بود.");
|
| 550 |
+
return english;
|
|
|
|
| 551 |
}
|
| 552 |
|
| 553 |
async function handleSuccessfulVideoGeneration(result) {
|
| 554 |
aiLoader.style.display = 'none';
|
| 555 |
+
if (!result?.data?.[0]?.video?.url) {
|
| 556 |
showCriticalError('پاسخ دریافتی از سرور ویدیو معتبر نیست.');
|
| 557 |
return;
|
| 558 |
}
|
|
|
|
| 651 |
|
| 652 |
lastAttemptedVideoPayload = { persianPrompt, imageSourceFile: imageFile, duration };
|
| 653 |
|
| 654 |
+
// ---- مرحله ترجمه با فالبک ----
|
| 655 |
+
let promptForVideo = persianPrompt;
|
| 656 |
try {
|
| 657 |
+
const englishPrompt = await translatePersianToEnglish(persianPrompt, { timeoutMs: TRANSLATION_TIMEOUT });
|
| 658 |
addStatusMessage(`ترجمه: ${englishPrompt}`, 'info');
|
| 659 |
+
promptForVideo = englishPrompt;
|
| 660 |
+
} catch (e) {
|
| 661 |
+
addStatusMessage('ترجمه به انگلیسی ناموفق/کند بود؛ با همان متن فارسی ادامه میدهیم.', 'error');
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
try {
|
| 665 |
setAiLoaderText("در حال ساخت ویدیو...");
|
|
|
|
| 666 |
const client = await Client.connect(VIDEO_SPACE_NAME);
|
| 667 |
|
| 668 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 669 |
+
setTimeout(() => reject(new Error('پردازش ویدیو بیش از 2.5 دقیقه طول کشید.')), GENERATION_TIMEOUT)
|
| 670 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 671 |
|
|
|
|
| 672 |
const videoGenerationPromise = client.predict(0, [
|
| 673 |
+
imageFile, // 0: input_image
|
| 674 |
+
promptForVideo, // 1: prompt
|
| 675 |
+
6, // 2: steps
|
| 676 |
+
DEFAULT_NEGATIVE_PROMPT, // 3: negative_prompt
|
| 677 |
+
duration, // 4: duration_seconds
|
| 678 |
+
1, // 5: guidance_scale
|
| 679 |
+
1, // 6: guidance_scale_2
|
| 680 |
+
0, // 7: seed (0 = random)
|
| 681 |
+
true // 8: randomize_seed
|
| 682 |
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 683 |
|
| 684 |
+
const result = await Promise.race([videoGenerationPromise, timeoutPromise]);
|
| 685 |
await handleSuccessfulVideoGeneration(result);
|
| 686 |
|
| 687 |
} catch (errorCaught) {
|
| 688 |
+
showCriticalError(`خطا: ${errorCaught.message}`);
|
| 689 |
+
} finally {
|
| 690 |
+
generateButton.disabled = false;
|
| 691 |
}
|
| 692 |
};
|
| 693 |
|