Update index.html
Browse files- index.html +327 -361
index.html
CHANGED
|
@@ -9,335 +9,207 @@
|
|
| 9 |
:root {
|
| 10 |
--app-font: 'Vazirmatn', sans-serif;
|
| 11 |
--app-bg: #F8F9FC;
|
| 12 |
-
--
|
| 13 |
-
--panel-bg: rgba(255, 255, 255, 0.9);
|
| 14 |
--panel-border: #EAEFF7;
|
| 15 |
--input-bg: #F6F8FB;
|
| 16 |
--input-border: #E1E7EF;
|
| 17 |
--text-primary: #1A202C;
|
| 18 |
-
--text-secondary: #
|
| 19 |
--text-tertiary: #8A94A6;
|
| 20 |
-
--accent-primary: #
|
| 21 |
-
--accent-primary-hover: #
|
| 22 |
-
--accent-primary-glow: rgba(
|
| 23 |
-
--accent-secondary: #
|
| 24 |
-
--
|
| 25 |
-
--
|
| 26 |
-
--danger-color: #
|
| 27 |
-
--
|
| 28 |
-
--shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.04);
|
| 29 |
--shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04);
|
| 30 |
-
--shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.
|
| 31 |
-
--shadow-xl: 0 20px 25px -5px rgba(26, 32, 44, 0.
|
| 32 |
-
--radius-card:
|
| 33 |
-
--radius-btn:
|
| 34 |
-
--radius-input:
|
| 35 |
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
}
|
| 37 |
|
| 38 |
-
|
| 39 |
-
@keyframes
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
@keyframes
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
@keyframes fadeInScaleUp { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
|
| 46 |
-
@keyframes fill-progress { from { width: 0%; } to { width: 100%; } }
|
| 47 |
-
|
| 48 |
-
body {
|
| 49 |
-
font-family: var(--app-font);
|
| 50 |
-
background: var(--app-bg-gradient);
|
| 51 |
-
color: var(--text-primary);
|
| 52 |
-
margin: 0;
|
| 53 |
-
padding: 3rem 1rem;
|
| 54 |
-
display: flex;
|
| 55 |
-
justify-content: center;
|
| 56 |
-
align-items: flex-start;
|
| 57 |
-
min-height: 100vh;
|
| 58 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
|
|
|
| 60 |
.container { max-width: 820px; width: 100%; }
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
-
/* ---
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
animation: fadeIn 0.8s ease-out backwards;
|
| 67 |
-
}
|
| 68 |
-
.aurora-visualizer {
|
| 69 |
-
width: 150px;
|
| 70 |
-
height: 150px;
|
| 71 |
margin: 0 auto 1.5rem;
|
| 72 |
position: relative;
|
| 73 |
display: flex;
|
| 74 |
align-items: center;
|
| 75 |
justify-content: center;
|
| 76 |
}
|
| 77 |
-
.
|
| 78 |
-
width:
|
| 79 |
-
height:
|
| 80 |
-
background: #
|
| 81 |
-
border-radius:
|
| 82 |
-
|
|
|
|
| 83 |
display: flex;
|
| 84 |
align-items: center;
|
| 85 |
justify-content: center;
|
|
|
|
| 86 |
position: relative;
|
| 87 |
z-index: 2;
|
| 88 |
-
animation: scale-up-down 4s ease-in-out infinite;
|
| 89 |
-
border: 1px solid rgba(225, 231, 239, 0.8);
|
| 90 |
}
|
| 91 |
-
.
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
border-radius: 50%;
|
| 97 |
-
filter: blur(25px);
|
| 98 |
-
opacity: 0.6;
|
| 99 |
-
mix-blend-mode: screen;
|
| 100 |
}
|
| 101 |
-
.
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
-webkit-background-clip: text;
|
| 110 |
-
-webkit-text-fill-color: transparent;
|
| 111 |
-
letter-spacing: -1.5px;
|
| 112 |
}
|
| 113 |
-
.
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
main {
|
| 117 |
-
padding: 3rem;
|
| 118 |
-
background: var(--panel-bg);
|
| 119 |
-
border-radius: var(--radius-card);
|
| 120 |
-
box-shadow: var(--shadow-xl);
|
| 121 |
-
border: 1px solid var(--panel-border);
|
| 122 |
-
animation: fadeIn 0.8s 0.2s ease-out backwards;
|
| 123 |
-
backdrop-filter: blur(10px);
|
| 124 |
}
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
.form-group { margin-bottom: 2.5rem; }
|
| 127 |
.form-group:last-child { margin-bottom: 0; }
|
| 128 |
-
.form-label { display: flex; align-items: center; gap: 0.75rem; font-weight: 700; color: var(--text-primary); font-size: 1.
|
| 129 |
-
.form-label svg { width:
|
| 130 |
-
|
| 131 |
-
#image-drop-zone {
|
| 132 |
-
position: relative;
|
| 133 |
-
border: 2px dashed var(--input-border);
|
| 134 |
-
border-radius: var(--radius-input);
|
| 135 |
-
padding: 2.5rem;
|
| 136 |
-
text-align: center;
|
| 137 |
-
cursor: pointer;
|
| 138 |
-
transition: var(--transition-smooth);
|
| 139 |
-
background-color: var(--input-bg);
|
| 140 |
-
overflow: hidden;
|
| 141 |
-
}
|
| 142 |
-
#image-drop-zone:hover, #image-drop-zone.drag-over {
|
| 143 |
-
border-color: var(--accent-primary);
|
| 144 |
-
background-color: #fff;
|
| 145 |
-
box-shadow: 0 0 20px var(--accent-primary-glow);
|
| 146 |
-
transform: scale(1.02);
|
| 147 |
-
}
|
| 148 |
#image-drop-zone.has-image { border-style: solid; border-color: var(--success-color); padding: 0; cursor: default; }
|
| 149 |
-
|
| 150 |
-
.upload-
|
| 151 |
-
|
| 152 |
#imagePreview { display: none; width: 100%; height: 100%; object-fit: contain; position: absolute; top: 0; left: 0; }
|
| 153 |
#image-drop-zone.has-image .upload-content { display: none; }
|
| 154 |
#image-drop-zone.has-image #imagePreview { display: block; }
|
| 155 |
-
|
| 156 |
-
textarea {
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
border: 1px solid var(--input-border);
|
| 161 |
-
background-color: var(--input-bg);
|
| 162 |
-
color: var(--text-primary);
|
| 163 |
-
box-shadow: var(--shadow-sm) inset;
|
| 164 |
-
font-family: var(--app-font);
|
| 165 |
-
font-size: 1.05rem;
|
| 166 |
-
box-sizing: border-box;
|
| 167 |
-
transition: var(--transition-smooth);
|
| 168 |
-
min-height: 120px;
|
| 169 |
-
resize: vertical;
|
| 170 |
-
}
|
| 171 |
-
textarea:focus {
|
| 172 |
-
outline: none;
|
| 173 |
-
border-color: var(--accent-primary);
|
| 174 |
-
box-shadow: 0 0 0 4px var(--accent-primary-glow), var(--shadow-sm) inset;
|
| 175 |
-
background-color: #fff;
|
| 176 |
-
}
|
| 177 |
-
|
| 178 |
-
#generateButton {
|
| 179 |
-
position: relative;
|
| 180 |
-
display: flex;
|
| 181 |
-
align-items: center;
|
| 182 |
-
justify-content: center;
|
| 183 |
-
gap: 0.75rem;
|
| 184 |
-
width: 100%;
|
| 185 |
-
padding: 1.2rem;
|
| 186 |
-
font-size: 1.25rem;
|
| 187 |
-
font-weight: 700;
|
| 188 |
-
background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%);
|
| 189 |
-
color: #fff;
|
| 190 |
-
border: none;
|
| 191 |
-
border-radius: var(--radius-btn);
|
| 192 |
-
cursor: pointer;
|
| 193 |
-
transition: all 0.3s ease;
|
| 194 |
-
box-shadow: 0 6px 15px -3px var(--accent-primary-glow), 0 6px 15px -3px var(--accent-secondary-glow);
|
| 195 |
-
margin-top: 1.5rem;
|
| 196 |
-
overflow: hidden;
|
| 197 |
-
}
|
| 198 |
-
#generateButton .icon-sparkle { transition: var(--transition-smooth); }
|
| 199 |
-
#generateButton:hover:not(:disabled) {
|
| 200 |
-
transform: translateY(-5px) scale(1.02);
|
| 201 |
-
box-shadow: 0 8px 25px -4px var(--accent-primary-glow), 0 8px 25px -4px var(--accent-secondary-glow);
|
| 202 |
-
}
|
| 203 |
-
#generateButton:hover:not(:disabled) .icon-sparkle { transform: rotate(15deg) scale(1.1); }
|
| 204 |
-
#generateButton::before { /* Shimmer Effect */
|
| 205 |
-
content: '';
|
| 206 |
-
position: absolute;
|
| 207 |
-
top: 0;
|
| 208 |
-
left: 0;
|
| 209 |
-
width: 50%;
|
| 210 |
-
height: 100%;
|
| 211 |
-
background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0) 100%);
|
| 212 |
-
transform: translateX(-150%) skewX(-20deg);
|
| 213 |
-
transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
| 214 |
-
}
|
| 215 |
-
#generateButton:hover:not(:disabled)::before { transform: translateX(250%) skewX(-20deg); }
|
| 216 |
#generateButton:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
|
| 217 |
-
|
| 218 |
-
#result-container {
|
| 219 |
-
|
| 220 |
-
position: relative;
|
| 221 |
-
background-color: var(--input-bg);
|
| 222 |
-
border-radius: var(--radius-card);
|
| 223 |
-
border: 2px dashed var(--input-border);
|
| 224 |
-
box-shadow: var(--shadow-sm) inset;
|
| 225 |
-
transition: var(--transition-smooth);
|
| 226 |
-
display: flex;
|
| 227 |
-
align-items: center;
|
| 228 |
-
justify-content: center;
|
| 229 |
-
padding: 1rem;
|
| 230 |
-
overflow: hidden;
|
| 231 |
-
}
|
| 232 |
-
#statusSection, #outputSection, #criticalErrorSection { display: none; width: 100%; text-align: center; }
|
| 233 |
#statusSection.active, #outputSection.active, #criticalErrorSection.active { display: block; animation: fadeIn 0.5s; }
|
| 234 |
-
#
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
height: 100%;
|
| 257 |
-
object-fit: cover;
|
| 258 |
-
filter: saturate(0.8) contrast(0.9);
|
| 259 |
-
}
|
| 260 |
-
.scan-line {
|
| 261 |
-
position: absolute;
|
| 262 |
-
left: 0;
|
| 263 |
-
width: 100%;
|
| 264 |
-
height: 4px;
|
| 265 |
-
background: linear-gradient(90deg, transparent, var(--accent-secondary), transparent);
|
| 266 |
-
box-shadow: 0 0 15px 2px var(--accent-secondary);
|
| 267 |
-
animation: scan-line-anim 3s ease-in-out infinite;
|
| 268 |
-
border-radius: 50%;
|
| 269 |
-
}
|
| 270 |
-
.glow-overlay {
|
| 271 |
-
position: absolute;
|
| 272 |
-
top: 0;
|
| 273 |
-
left: 0;
|
| 274 |
-
width: 100%;
|
| 275 |
-
height: 100%;
|
| 276 |
-
background: radial-gradient(circle at center, var(--accent-primary) 0%, transparent 70%);
|
| 277 |
-
animation: glow-pulse 4s ease-in-out infinite;
|
| 278 |
-
}
|
| 279 |
-
.processing-text {
|
| 280 |
-
position: relative;
|
| 281 |
-
z-index: 2;
|
| 282 |
-
color: #fff;
|
| 283 |
-
font-size: 1.2rem;
|
| 284 |
-
font-weight: 600;
|
| 285 |
-
text-shadow: 0 2px 10px #000;
|
| 286 |
-
padding: 1rem;
|
| 287 |
-
width: 100%;
|
| 288 |
-
background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
|
| 289 |
-
}
|
| 290 |
-
.progress-bar {
|
| 291 |
-
position: absolute; bottom: 0; left: 0; width: 0%; height: 5px;
|
| 292 |
-
background: linear-gradient(to right, var(--accent-secondary), var(--accent-primary));
|
| 293 |
-
transition: width 1s ease-out;
|
| 294 |
-
}
|
| 295 |
.progress-bar.animate-progress { animation: fill-progress 60s linear forwards; }
|
| 296 |
-
|
| 297 |
#outputVideo { width: 100%; max-width: 500px; border-radius: var(--radius-input); margin: 1rem auto; display: block; background-color: #000; box-shadow: var(--shadow-md); }
|
| 298 |
-
#finalSeed { text-align: center; color: var(--text-tertiary); font-size: 0.
|
| 299 |
-
|
| 300 |
.video-controls { display: flex; justify-content: center; gap: 1rem; margin-top: 1.5rem; }
|
| 301 |
-
.video-button { padding: 0.
|
| 302 |
-
.video-button:hover:not(:disabled) { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
|
| 303 |
.video-button.primary { background-color: var(--accent-primary); color: white; }
|
| 304 |
-
.video-button.primary:hover { background-color: var(--accent-primary-hover); }
|
| 305 |
-
.video-button:not(.primary) { background-color:
|
| 306 |
-
.video-button:not(.primary):hover { background-color: var(--
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
.gpu-error-message-container
|
| 310 |
-
.gpu-error-message-container
|
|
|
|
|
|
|
|
|
|
| 311 |
.gpu-error-actions { display: flex; gap: 15px; margin-top: 25px; }
|
| 312 |
-
.gpu-error-actions .action-button { padding: 12px 20px; border: none; border-radius:
|
| 313 |
.gpu-error-actions .action-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
|
| 314 |
-
.gpu-error-actions .action-button.back-button { background-color:
|
| 315 |
-
.gpu-error-actions .action-button.
|
|
|
|
|
|
|
| 316 |
|
| 317 |
@media (max-width: 768px) {
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
}
|
| 322 |
</style>
|
| 323 |
</head>
|
| 324 |
<body>
|
| 325 |
<div class="container">
|
| 326 |
<header>
|
| 327 |
-
<
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
<
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
</div>
|
| 335 |
-
<
|
| 336 |
-
<
|
|
|
|
| 337 |
</div>
|
| 338 |
-
<h1>متحرک سازی تصاویر</h1>
|
| 339 |
-
<p class="subtitle">به تصاویر ثابت خود جان ببخشید و آنها را با هوش مصنوعی متحرک کنید</p>
|
| 340 |
-
<p class="instruction">تصویر خود را آپلود کنید، توضیح دهید چگونه باید حرکت کند، و جادو را تماشا کنید.</p>
|
| 341 |
</header>
|
| 342 |
|
| 343 |
<main>
|
|
@@ -363,28 +235,30 @@
|
|
| 363 |
</label>
|
| 364 |
<textarea id="prompt" rows="4" placeholder="مثال: حرکت ابرها، موج زدن آب، وزش ملایم باد در موها، زوم آهسته به بیرون"></textarea>
|
| 365 |
<button id="generateButton">
|
| 366 |
-
<svg
|
| 367 |
<span>شروع متحرک سازی</span>
|
| 368 |
</button>
|
| 369 |
</div>
|
| 370 |
|
| 371 |
<div class="form-group">
|
| 372 |
<div class="form-label">
|
| 373 |
-
<svg xmlns="http://www.w3.org/2000/svg"
|
| 374 |
۳. نتیجه را ببینید
|
| 375 |
</div>
|
| 376 |
<div id="result-container">
|
| 377 |
<div id="statusSection">
|
| 378 |
-
<
|
| 379 |
<div id="aiLoader" style="display: none;">
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
| 388 |
</div>
|
| 389 |
|
| 390 |
<div id="outputSection">
|
|
@@ -427,10 +301,10 @@
|
|
| 427 |
const finalSeedElement = document.getElementById('finalSeed');
|
| 428 |
const criticalErrorSection = document.getElementById('criticalErrorSection');
|
| 429 |
const statusSection = document.getElementById('statusSection');
|
|
|
|
| 430 |
const aiLoader = document.getElementById('aiLoader');
|
| 431 |
-
const
|
| 432 |
-
const
|
| 433 |
-
const loaderTextOverlay = document.querySelector('#imageAnimatorLoader .processing-text');
|
| 434 |
const outputSection = document.getElementById('outputSection');
|
| 435 |
const btnRestart = document.getElementById('btnRestart');
|
| 436 |
const btnDownloadVideo = document.getElementById('btnDownloadVideo');
|
|
@@ -440,7 +314,7 @@
|
|
| 440 |
const TRANSLATOR_SPACE_URL_BASE = "https://hamed744-translate-tts-aloha.hf.space/gradio_api";
|
| 441 |
const FN_INDEX_TRANSLATE_SPEAK = 1;
|
| 442 |
const DEFAULT_TTS_VOICE_TRANSLATOR = "انگلیسی (آمریکا) - جنی (زن)";
|
| 443 |
-
const DEFAULT_NEGATIVE_PROMPT = "bad quality, ugly, blurry, watermark, text, signature
|
| 444 |
const DEFAULT_PROMPT_TEXT = "";
|
| 445 |
const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
| 446 |
const DEFAULT_VIDEO_DURATION = 5.1;
|
|
@@ -453,24 +327,6 @@
|
|
| 453 |
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15).substring(0, length - 7);
|
| 454 |
}
|
| 455 |
|
| 456 |
-
function setAiLoaderText(text) {
|
| 457 |
-
if (loaderTextOverlay) loaderTextOverlay.textContent = text;
|
| 458 |
-
}
|
| 459 |
-
|
| 460 |
-
function clearStatus() {
|
| 461 |
-
aiLoader.style.display = 'none';
|
| 462 |
-
if(loaderProgressBar) {
|
| 463 |
-
loaderProgressBar.classList.remove('animate-progress');
|
| 464 |
-
loaderProgressBar.style.width = '0%';
|
| 465 |
-
}
|
| 466 |
-
if (outputVideo.src && outputVideo.src.startsWith('blob:')) {
|
| 467 |
-
URL.revokeObjectURL(outputVideo.src);
|
| 468 |
-
}
|
| 469 |
-
outputVideo.src = '';
|
| 470 |
-
finalSeedElement.textContent = '';
|
| 471 |
-
btnDownloadVideo.style.display = 'none';
|
| 472 |
-
}
|
| 473 |
-
|
| 474 |
async function retryLastAttempt() {
|
| 475 |
if (!lastAttemptedVideoPayload || !lastAttemptedVideoPayload.imageSourceFile || !lastAttemptedVideoPayload.persianPrompt) {
|
| 476 |
showCriticalError("اطلاعات کافی برای تلاش مجدد وجود ندارد. لطفاً از نو شروع کنید.");
|
|
@@ -478,19 +334,21 @@
|
|
| 478 |
}
|
| 479 |
|
| 480 |
hideCriticalError();
|
| 481 |
-
|
| 482 |
generateButton.disabled = true;
|
| 483 |
resultContainer.classList.add('active');
|
| 484 |
statusSection.classList.add('active');
|
| 485 |
-
aiLoader.style.display = '
|
| 486 |
-
loaderImagePreview.src = imagePreview.src;
|
| 487 |
loaderProgressBar.classList.add('animate-progress');
|
| 488 |
outputSection.classList.remove('active');
|
|
|
|
| 489 |
|
| 490 |
const { persianPrompt, imageSourceFile } = lastAttemptedVideoPayload;
|
| 491 |
|
| 492 |
try {
|
| 493 |
const englishPromptForVideo = await translatePersianToEnglish(persianPrompt);
|
|
|
|
|
|
|
| 494 |
setAiLoaderText("در حال متحرک سازی تصویر...");
|
| 495 |
|
| 496 |
const client = await Client.connect(VIDEO_SPACE_NAME);
|
|
@@ -514,41 +372,41 @@
|
|
| 514 |
}
|
| 515 |
}
|
| 516 |
|
| 517 |
-
function showCriticalError(message) {
|
| 518 |
const lowerCaseMessage = (message || "").toLowerCase();
|
| 519 |
-
|
| 520 |
-
if (lowerCaseMessage.includes("gpu") || lowerCaseMessage.includes("quota") || lowerCaseMessage.includes("capacity") || lowerCaseMessage.includes("queue full")) {
|
| 521 |
-
|
| 522 |
<div class="gpu-error-message-container">
|
| 523 |
-
<h2>💡 راهنمای رفع محدودیت انیمیشن</h2>
|
| 524 |
-
<div class="error-content"
|
| 525 |
-
<
|
| 526 |
-
<p>این
|
| 527 |
-
<
|
| 528 |
-
|
| 529 |
-
<li>گ
|
| 530 |
-
<li>اگر ا
|
|
|
|
| 531 |
</ul>
|
| 532 |
-
<p>با این
|
| 533 |
</div>
|
| 534 |
<div class="gpu-error-actions">
|
| 535 |
<button class="action-button back-button" id="gpuErrorGoBackBtn">بازگشت</button>
|
| 536 |
<button class="action-button retry-button" id="gpuErrorRetryBtn">تلاش مجدد</button>
|
| 537 |
</div>
|
| 538 |
</div>`;
|
| 539 |
-
criticalErrorSection.innerHTML =
|
| 540 |
document.getElementById('gpuErrorGoBackBtn').addEventListener('click', hideCriticalError);
|
| 541 |
document.getElementById('gpuErrorRetryBtn').addEventListener('click', retryLastAttempt);
|
| 542 |
} else {
|
| 543 |
-
|
| 544 |
-
<div style="color: var(--danger-color); font-weight: 500; margin-bottom: 1.5rem; line-height: 1.6;"><p>${message}</p></div>
|
| 545 |
<div class="video-controls">
|
| 546 |
<button id="simpleErrorGoBackBtn" class="video-button">بازگشت</button>
|
| 547 |
<button id="simpleErrorRetryBtn" class="video-button primary">تلاش مجدد</button>
|
| 548 |
</div>`;
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
document.getElementById('simpleErrorRetryBtn').addEventListener('click', retryLastAttempt);
|
| 552 |
}
|
| 553 |
|
| 554 |
resultContainer.classList.add('active');
|
|
@@ -564,12 +422,49 @@
|
|
| 564 |
criticalErrorSection.innerHTML = '';
|
| 565 |
}
|
| 566 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
function initializeForm() {
|
| 568 |
hideCriticalError();
|
| 569 |
resultContainer.classList.remove('active');
|
| 570 |
statusSection.classList.remove('active');
|
| 571 |
outputSection.classList.remove('active');
|
| 572 |
-
|
| 573 |
lastAttemptedVideoPayload = null;
|
| 574 |
imageFileInput.value = '';
|
| 575 |
imagePreview.src = TRANSPARENT_PIXEL_SRC;
|
|
@@ -579,23 +474,52 @@
|
|
| 579 |
}
|
| 580 |
|
| 581 |
async function translatePersianToEnglish(persianText) {
|
| 582 |
-
setAiLoaderText("در حال ترجمه دستور...");
|
| 583 |
let currentTranslatorSessionHash = generateRandomHash();
|
| 584 |
-
const payload = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
try {
|
| 586 |
-
await fetch(`${TRANSLATOR_SPACE_URL_BASE}/queue/join?`, {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 587 |
return new Promise((resolve, reject) => {
|
| 588 |
const eventSource = new EventSource(`${TRANSLATOR_SPACE_URL_BASE}/queue/data?session_hash=${currentTranslatorSessionHash}`);
|
| 589 |
-
eventSource.onmessage = (event)
|
| 590 |
const data = JSON.parse(event.data);
|
| 591 |
if (data.msg === "process_completed") {
|
| 592 |
eventSource.close();
|
| 593 |
-
data.success
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
}
|
| 595 |
};
|
| 596 |
-
eventSource.onerror = () => {
|
|
|
|
|
|
|
|
|
|
| 597 |
});
|
| 598 |
-
} catch (error) {
|
|
|
|
|
|
|
|
|
|
| 599 |
}
|
| 600 |
|
| 601 |
function handleSuccessfulVideoGeneration(result) {
|
|
@@ -604,15 +528,19 @@
|
|
| 604 |
showCriticalError('پاسخ دریافتی از سرور انیمیشن معتبر نیست.');
|
| 605 |
return;
|
| 606 |
}
|
|
|
|
|
|
|
| 607 |
const videoUrl = result.data[0].video.url;
|
| 608 |
const usedSeed = result.data[1];
|
| 609 |
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
|
|
|
|
|
|
| 616 |
|
| 617 |
generateButton.disabled = false;
|
| 618 |
}
|
|
@@ -632,25 +560,29 @@
|
|
| 632 |
|
| 633 |
// Event Listeners
|
| 634 |
document.addEventListener('DOMContentLoaded', initializeForm);
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
['dragenter', 'dragover'
|
| 638 |
-
['
|
| 639 |
-
['dragleave', 'drop'].forEach(eventName => imageDropZone.addEventListener(eventName, () => imageDropZone.classList.remove('drag-over')));
|
| 640 |
imageDropZone.addEventListener('drop', e => {
|
| 641 |
-
if (e.dataTransfer.files.length > 0) {
|
| 642 |
imageFileInput.files = e.dataTransfer.files;
|
| 643 |
-
|
|
|
|
| 644 |
}
|
| 645 |
});
|
|
|
|
|
|
|
| 646 |
|
| 647 |
imageFileInput.addEventListener('change', function(event) {
|
| 648 |
const file = event.target.files[0];
|
| 649 |
hideCriticalError();
|
| 650 |
if (file) {
|
| 651 |
if (!file.type.startsWith('image/')) {
|
| 652 |
-
|
| 653 |
imageFileInput.value = '';
|
|
|
|
|
|
|
| 654 |
} else {
|
| 655 |
const reader = new FileReader();
|
| 656 |
reader.onload = (e) => {
|
|
@@ -669,23 +601,35 @@
|
|
| 669 |
const persianPrompt = promptInput.value.trim();
|
| 670 |
const imageFile = imageFileInput.files[0];
|
| 671 |
|
| 672 |
-
if (!persianPrompt) {
|
| 673 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 674 |
|
| 675 |
-
|
| 676 |
hideCriticalError();
|
| 677 |
generateButton.disabled = true;
|
| 678 |
resultContainer.classList.add('active');
|
| 679 |
statusSection.classList.add('active');
|
| 680 |
-
aiLoader.style.display = '
|
| 681 |
-
loaderImagePreview.src = imagePreview.src; // Set image for the new loader
|
| 682 |
loaderProgressBar.classList.add('animate-progress');
|
| 683 |
outputSection.classList.remove('active');
|
|
|
|
| 684 |
|
| 685 |
-
lastAttemptedVideoPayload = {
|
|
|
|
|
|
|
|
|
|
| 686 |
|
| 687 |
try {
|
| 688 |
const englishPrompt = await translatePersianToEnglish(persianPrompt);
|
|
|
|
|
|
|
| 689 |
setAiLoaderText("در حال متحرک سازی تصویر...");
|
| 690 |
|
| 691 |
const client = await Client.connect(VIDEO_SPACE_NAME);
|
|
@@ -704,10 +648,32 @@
|
|
| 704 |
handleSuccessfulVideoGeneration(result);
|
| 705 |
|
| 706 |
} catch (errorCaught) {
|
| 707 |
-
|
|
|
|
| 708 |
}
|
| 709 |
});
|
| 710 |
})();
|
| 711 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
</body>
|
| 713 |
</html>
|
|
|
|
| 9 |
:root {
|
| 10 |
--app-font: 'Vazirmatn', sans-serif;
|
| 11 |
--app-bg: #F8F9FC;
|
| 12 |
+
--panel-bg: #FFFFFF;
|
|
|
|
| 13 |
--panel-border: #EAEFF7;
|
| 14 |
--input-bg: #F6F8FB;
|
| 15 |
--input-border: #E1E7EF;
|
| 16 |
--text-primary: #1A202C;
|
| 17 |
+
--text-secondary: #626F86;
|
| 18 |
--text-tertiary: #8A94A6;
|
| 19 |
+
--accent-primary: #4A6CFA;
|
| 20 |
+
--accent-primary-hover: #3553D6;
|
| 21 |
+
--accent-primary-glow: rgba(74, 108, 250, 0.25);
|
| 22 |
+
--accent-secondary: #0FD4A8;
|
| 23 |
+
--success-color: #38A169;
|
| 24 |
+
--danger-color: #e53e3e;
|
| 25 |
+
--danger-color-hover: #c53030;
|
| 26 |
+
--shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.03);
|
|
|
|
| 27 |
--shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04);
|
| 28 |
+
--shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06), 0 4px 6px -4px rgba(26, 32, 44, 0.05);
|
| 29 |
+
--shadow-xl: 0 20px 25px -5px rgba(26, 32, 44, 0.07), 0 8px 10px -6px rgba(26, 32, 44, 0.05);
|
| 30 |
+
--radius-card: 24px;
|
| 31 |
+
--radius-btn: 14px;
|
| 32 |
+
--radius-input: 12px;
|
| 33 |
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
| 34 |
+
|
| 35 |
+
--igadlm-alpha-color-primary: #5A67D8;
|
| 36 |
+
--igadlm-alpha-color-secondary: #805AD5;
|
| 37 |
+
--igadlm-alpha-color-primary-darker: #4C51BF;
|
| 38 |
+
--igadlm-alpha-color-secondary-darker: #6B46C1;
|
| 39 |
+
--igadlm-alpha-color-text-light: #4A5568;
|
| 40 |
+
--igadlm-alpha-color-warning-bg: #FFFBEB;
|
| 41 |
+
--igadlm-alpha-color-warning-border: #FBBF24;
|
| 42 |
+
--igadlm-alpha-color-warning-text: #B45309;
|
| 43 |
+
--igadlm-alpha-border-radius-md: 8px;
|
| 44 |
+
--igadlm-alpha-border-radius-lg: 12px;
|
| 45 |
}
|
| 46 |
|
| 47 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
|
| 48 |
+
@keyframes igadlmAlphaV2fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
| 49 |
+
|
| 50 |
+
/* --- انیمیشن جدید برای هدر --- */
|
| 51 |
+
@keyframes pulse-glow {
|
| 52 |
+
0%, 100% { box-shadow: 0 0 20px var(--accent-primary-glow), 0 0 30px rgba(15, 212, 168, 0.2); }
|
| 53 |
+
50% { box-shadow: 0 0 35px rgba(74, 108, 250, 0.4), 0 0 50px rgba(15, 212, 168, 0.3); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
+
@keyframes wave {
|
| 56 |
+
0% { transform: scale(0.5); opacity: 1; }
|
| 57 |
+
100% { transform: scale(2.5); opacity: 0; }
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/* --- انیمیشن برای نوار پیشرفت --- */
|
| 61 |
+
@keyframes fill-progress { from { width: 0%; } to { width: 100%; } }
|
| 62 |
|
| 63 |
+
body { font-family: var(--app-font); background-color: var(--app-bg); color: var(--text-primary); margin: 0; padding: 2.5rem 1rem; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; }
|
| 64 |
.container { max-width: 820px; width: 100%; }
|
| 65 |
+
header { position: relative; text-align: center; margin-bottom: 2.5rem; padding: 2rem 0; animation: fadeIn 0.8s 0.1s ease-out backwards; overflow: hidden; }
|
| 66 |
+
#neural-network-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; opacity: 0.7; }
|
| 67 |
+
.header-content { position: relative; z-index: 2; }
|
| 68 |
|
| 69 |
+
/* --- استایل انیمیشن جدید هدر --- */
|
| 70 |
+
.animation-visualizer {
|
| 71 |
+
width: 140px;
|
| 72 |
+
height: 140px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
margin: 0 auto 1.5rem;
|
| 74 |
position: relative;
|
| 75 |
display: flex;
|
| 76 |
align-items: center;
|
| 77 |
justify-content: center;
|
| 78 |
}
|
| 79 |
+
.image-placeholder {
|
| 80 |
+
width: 90px;
|
| 81 |
+
height: 90px;
|
| 82 |
+
background: linear-gradient(45deg, #e0e7ff, #f0f9ff);
|
| 83 |
+
border-radius: 20px;
|
| 84 |
+
border: 2px solid rgba(74, 108, 250, 0.2);
|
| 85 |
+
box-shadow: 0 8px 25px rgba(26, 32, 44, 0.08);
|
| 86 |
display: flex;
|
| 87 |
align-items: center;
|
| 88 |
justify-content: center;
|
| 89 |
+
animation: pulse-glow 5s ease-in-out infinite;
|
| 90 |
position: relative;
|
| 91 |
z-index: 2;
|
|
|
|
|
|
|
| 92 |
}
|
| 93 |
+
.image-placeholder svg {
|
| 94 |
+
width: 40px;
|
| 95 |
+
height: 40px;
|
| 96 |
+
color: var(--accent-primary);
|
| 97 |
+
opacity: 0.8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
+
.wave-ring {
|
| 100 |
+
position: absolute;
|
| 101 |
+
width: 90px;
|
| 102 |
+
height: 90px;
|
| 103 |
+
border-radius: 20px;
|
| 104 |
+
border: 2px solid var(--accent-primary);
|
| 105 |
+
animation: wave 3s infinite cubic-bezier(0.65, 0, 0.35, 1);
|
| 106 |
+
z-index: 1;
|
|
|
|
|
|
|
|
|
|
| 107 |
}
|
| 108 |
+
.wave-ring.two {
|
| 109 |
+
animation-delay: -1.5s;
|
| 110 |
+
border-color: var(--accent-secondary);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
}
|
| 112 |
+
|
| 113 |
+
h1 { font-size: 2.8rem; font-weight: 800; margin: 0; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -1px; }
|
| 114 |
+
.subtitle { font-size: 1.1rem; color: var(--text-secondary); margin-top: 0.5rem; }
|
| 115 |
+
.instruction { font-size: 0.95rem; color: var(--text-tertiary); margin-top: 0.75rem; font-weight: 500; }
|
| 116 |
+
main { padding: 3rem; background-color: var(--panel-bg); border-radius: var(--radius-card); box-shadow: var(--shadow-xl); border: 1px solid var(--panel-border); animation: fadeIn 0.8s 0.3s ease-out backwards; }
|
| 117 |
.form-group { margin-bottom: 2.5rem; }
|
| 118 |
.form-group:last-child { margin-bottom: 0; }
|
| 119 |
+
.form-label { display: flex; align-items: center; gap: 0.75rem; font-weight: 700; color: var(--text-primary); font-size: 1.2em; margin-bottom: 1.2rem; }
|
| 120 |
+
.form-label svg { width: 24px; height: 24px; color: var(--accent-primary); }
|
| 121 |
+
#image-drop-zone { position: relative; border: 2px dashed var(--input-border); border-radius: var(--radius-input); padding: 2.5rem; text-align: center; cursor: pointer; transition: var(--transition-smooth); background-color: var(--input-bg); min-height: 200px; display: flex; flex-direction: column; justify-content: center; align-items: center; overflow: hidden; }
|
| 122 |
+
#image-drop-zone.drag-over, #image-drop-zone:hover:not(.has-image) { border-color: var(--accent-primary); background-color: #fff; box-shadow: 0 0 15px var(--accent-primary-glow); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
#image-drop-zone.has-image { border-style: solid; border-color: var(--success-color); padding: 0; cursor: default; }
|
| 124 |
+
.upload-content { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
|
| 125 |
+
.upload-icon svg { width: 48px; height: 48px; color: var(--accent-primary); stroke-width: 1.5; opacity: 0.8; }
|
| 126 |
+
#image-drop-zone p { margin: 0; color: var(--text-secondary); font-weight: 500; }
|
| 127 |
#imagePreview { display: none; width: 100%; height: 100%; object-fit: contain; position: absolute; top: 0; left: 0; }
|
| 128 |
#image-drop-zone.has-image .upload-content { display: none; }
|
| 129 |
#image-drop-zone.has-image #imagePreview { display: block; }
|
| 130 |
+
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; }
|
| 131 |
+
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); }
|
| 132 |
+
#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: 1.5rem; }
|
| 133 |
+
#generateButton svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); }
|
| 134 |
+
#generateButton:hover:not(:disabled) { transform: translateY(-5px) scale(1.02); box-shadow: 0 8px 20px -4px var(--accent-primary-glow), 0 8px 20px -4px rgba(15, 212, 168, 0.3); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
#generateButton:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
|
| 136 |
+
#result-container { min-height: 350px; position: relative; padding: 1rem; background-color: var(--input-bg); border-radius: var(--radius-card); border: 2px dashed var(--input-border); box-shadow: var(--shadow-sm) inset; transition: var(--transition-smooth); display: flex; flex-direction: column; align-items: center; justify-content: center; }
|
| 137 |
+
#result-container.active { border-style: solid; border-color: var(--panel-border); }
|
| 138 |
+
#statusSection, #outputSection, #criticalErrorSection { display: none; width: 100%; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
#statusSection.active, #outputSection.active, #criticalErrorSection.active { display: block; animation: fadeIn 0.5s; }
|
| 140 |
+
#statusMessages { max-height: 150px; overflow-y: auto; width: 100%; padding: 0.5rem; margin-bottom: 1rem; }
|
| 141 |
+
.status-message { display: flex; align-items: center; gap: 0.75rem; padding: 0.6rem 1rem; margin-bottom: 0.5rem; border-radius: var(--radius-input); font-size: 0.9rem; background: var(--panel-bg); border: 1px solid var(--panel-border); color: var(--text-secondary); }
|
| 142 |
+
.status-message.success { border-left: 4px solid var(--success-color); color: var(--success-color); }
|
| 143 |
+
.status-message.error { border-left: 4px solid var(--danger-color); color: var(--danger-color); }
|
| 144 |
+
.status-icon { width: 18px; height: 18px; }
|
| 145 |
+
#aiLoader { display: none; align-items: center; justify-content: center; }
|
| 146 |
+
.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; }
|
| 147 |
+
@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); } }
|
| 148 |
+
.noise-layer, .sketch-layer, .building-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
| 149 |
+
.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; }
|
| 150 |
+
@keyframes fade-noise { 0% { opacity: 1; filter: blur(5px); } 30% { opacity: 0.8; filter: blur(2px); } 100% { opacity: 0; filter: blur(0px); } }
|
| 151 |
+
.sketch-layer { filter: grayscale(1) contrast(1.5) blur(3px); opacity: 0; animation: reveal-sketch 7s infinite ease-in-out; }
|
| 152 |
+
@keyframes reveal-sketch { 0% { opacity: 0; } 20% { opacity: 1; } 60% { opacity: 0.5; } 100% { opacity: 0; } }
|
| 153 |
+
.building-layer { filter: blur(15px); opacity: 0; animation: denoise-color 7s infinite ease-in-out; }
|
| 154 |
+
@keyframes denoise-color { 0% { opacity: 0; } 40% { opacity: 0.6; filter: blur(5px); } 100% { opacity: 1; filter: blur(0px); } }
|
| 155 |
+
.pixel-grid { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient(0deg, transparent 0 1px, rgba(255,255,255,0.1) 1px 2px), repeating-linear-gradient(90deg, transparent 0 1px, rgba(255,255,255,0.1) 1px 2px); opacity: 1; animation: dissolve-grid 7s infinite ease-in-out; }
|
| 156 |
+
@keyframes dissolve-grid { 0% { opacity: 1; } 70% { opacity: 0.5; } 100% { opacity: 0; } }
|
| 157 |
+
.particles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle, rgba(56, 189, 248, 0.2) 0%, transparent 50%); animation: flow-particles 7s infinite cubic-bezier(0.4, 0, 0.6, 1); }
|
| 158 |
+
@keyframes flow-particles { 0% { transform: translate(0, 0) scale(1); opacity: 0.5; } 50% { transform: translate(10px, -15px) scale(1.05); opacity: 0.8; } 100% { transform: translate(0, 0) scale(1); opacity: 0.5; } }
|
| 159 |
+
.text-overlay { position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%); font-size: 24px; font-weight: 700; text-shadow: 0 0 20px rgba(56, 189, 248, 0.8); animation: glow-text 7s infinite ease-in-out; font-family: var(--app-font); width: 90%; text-align: center;}
|
| 160 |
+
@keyframes glow-text { 0% { opacity: 0.7; } 50% { opacity: 1; } 100% { opacity: 0.7; } }
|
| 161 |
+
.progress-bar { position: absolute; bottom: 0; left: 0; width: 0%; height: 6px; background: linear-gradient(to right, #38bdf8, #bb86fc, #facc15); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
.progress-bar.animate-progress { animation: fill-progress 60s linear forwards; }
|
|
|
|
| 163 |
#outputVideo { width: 100%; max-width: 500px; border-radius: var(--radius-input); margin: 1rem auto; display: block; background-color: #000; box-shadow: var(--shadow-md); }
|
| 164 |
+
#finalSeed { text-align: center; color: var(--text-tertiary); font-size: 0.85rem; margin-top: 1rem; }
|
|
|
|
| 165 |
.video-controls { display: flex; justify-content: center; gap: 1rem; margin-top: 1.5rem; }
|
| 166 |
+
.video-button { padding: 0.7rem 1.5rem; border-radius: var(--radius-btn); border: none; cursor: pointer; font-family: var(--app-font); font-weight: 600; font-size: 1rem; transition: var(--transition-smooth); display: flex; align-items: center; gap: 0.5rem; }
|
|
|
|
| 167 |
.video-button.primary { background-color: var(--accent-primary); color: white; }
|
| 168 |
+
.video-button.primary:hover { background-color: var(--accent-primary-hover); transform: translateY(-2px); }
|
| 169 |
+
.video-button:not(.primary) { background-color: var(--input-bg); color: var(--text-secondary); border: 1px solid var(--input-border); }
|
| 170 |
+
.video-button:not(.primary):hover { background-color: var(--panel-border); color: var(--text-primary); }
|
| 171 |
+
.gpu-error-message-container { text-align: right; background-color: var(--igadlm-alpha-color-warning-bg); padding: 25px; border-radius: var(--igadlm-alpha-border-radius-lg); box-shadow: var(--shadow-lg); border: 2px solid var(--igadlm-alpha-color-warning-border); animation: igadlmAlphaV2fadeInUp 0.5s cubic-bezier(0.4, 0, 0.2, 1) both; }
|
| 172 |
+
.gpu-error-message-container h2 { font-size: 1.2em; color: var(--igadlm-alpha-color-warning-text); margin-bottom: 15px; font-weight: 700; display: flex; align-items: center; border-bottom: 1px solid var(--igadlm-alpha-color-warning-border); padding-bottom: 12px; }
|
| 173 |
+
.gpu-error-message-container h2 .icon { margin-left: 10px; font-size: 1.3em; }
|
| 174 |
+
.gpu-error-message-container .error-content { font-size: 0.9rem; color: var(--igadlm-alpha-color-text-light); line-height: 1.7; word-break: break-word; white-space: pre-line; }
|
| 175 |
+
.gpu-error-message-container .error-content strong.error-message-title { font-weight: 600; color: var(--igadlm-alpha-color-warning-text); display: block; margin-bottom: 8px;}
|
| 176 |
+
.gpu-error-message-container .error-content ul { list-style-type: "▫️ "; padding-right: 20px; margin-top: 10px; }
|
| 177 |
+
.gpu-error-message-container .error-content li { margin-bottom: 6px; }
|
| 178 |
.gpu-error-actions { display: flex; gap: 15px; margin-top: 25px; }
|
| 179 |
+
.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; }
|
| 180 |
.gpu-error-actions .action-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
|
| 181 |
+
.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; }
|
| 182 |
+
.gpu-error-actions .action-button.back-button:hover:not(:disabled) { background-color: var(--input-bg); }
|
| 183 |
+
.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; }
|
| 184 |
+
.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%); }
|
| 185 |
|
| 186 |
@media (max-width: 768px) {
|
| 187 |
+
main { padding: 1.5rem; } h1 { font-size: 2.2rem; }
|
| 188 |
+
.generator-container { height: 250px; }
|
| 189 |
+
.text-overlay { font-size: 18px; }
|
| 190 |
}
|
| 191 |
</style>
|
| 192 |
</head>
|
| 193 |
<body>
|
| 194 |
<div class="container">
|
| 195 |
<header>
|
| 196 |
+
<canvas id="neural-network-canvas"></canvas>
|
| 197 |
+
<div class="header-content">
|
| 198 |
+
<div class="animation-visualizer">
|
| 199 |
+
<div class="image-placeholder">
|
| 200 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
| 201 |
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
| 202 |
+
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
| 203 |
+
<polyline points="21 15 16 10 5 21"></polyline>
|
| 204 |
+
</svg>
|
| 205 |
+
</div>
|
| 206 |
+
<div class="wave-ring"></div>
|
| 207 |
+
<div class="wave-ring two"></div>
|
| 208 |
</div>
|
| 209 |
+
<h1>متحرک سازی تصاویر</h1>
|
| 210 |
+
<p class="subtitle">به تصاویر ثابت خود جان ببخشید و آنها را با هوش مصنوعی متحرک کنید</p>
|
| 211 |
+
<p class="instruction">تصویر خود را آپلود کنید، توضیح دهید چگونه باید حرکت کند، و جادو را تماشا کنید.</p>
|
| 212 |
</div>
|
|
|
|
|
|
|
|
|
|
| 213 |
</header>
|
| 214 |
|
| 215 |
<main>
|
|
|
|
| 235 |
</label>
|
| 236 |
<textarea id="prompt" rows="4" placeholder="مثال: حرکت ابرها، موج زدن آب، وزش ملایم باد در موها، زوم آهسته به بیرون"></textarea>
|
| 237 |
<button id="generateButton">
|
| 238 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L12 8L17 10L12 12L10 17L8 12L3 10L8 8L10 3z"/></svg>
|
| 239 |
<span>شروع متحرک سازی</span>
|
| 240 |
</button>
|
| 241 |
</div>
|
| 242 |
|
| 243 |
<div class="form-group">
|
| 244 |
<div class="form-label">
|
| 245 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L12 8L17 10L12 12L10 17L8 12L3 10L8 8L10 3z"/><path d="M21 14l-1.5 3-3-1.5 3-3 1.5 3z"/><path d="M19.5 2.5l-3 1.5 1.5 3 3-1.5-1.5-3z"/></svg>
|
| 246 |
۳. نتیجه را ببینید
|
| 247 |
</div>
|
| 248 |
<div id="result-container">
|
| 249 |
<div id="statusSection">
|
| 250 |
+
<div id="statusMessages"></div>
|
| 251 |
<div id="aiLoader" style="display: none;">
|
| 252 |
+
<div class="generator-container">
|
| 253 |
+
<div class="noise-layer"></div>
|
| 254 |
+
<div class="sketch-layer"></div>
|
| 255 |
+
<div class="building-layer"></div>
|
| 256 |
+
<div class="pixel-grid"></div>
|
| 257 |
+
<div class="particles"></div>
|
| 258 |
+
<div class="text-overlay">در حال متحرک سازی تصویر...</div>
|
| 259 |
+
<div class="progress-bar"></div>
|
| 260 |
+
</div>
|
| 261 |
+
</div>
|
| 262 |
</div>
|
| 263 |
|
| 264 |
<div id="outputSection">
|
|
|
|
| 301 |
const finalSeedElement = document.getElementById('finalSeed');
|
| 302 |
const criticalErrorSection = document.getElementById('criticalErrorSection');
|
| 303 |
const statusSection = document.getElementById('statusSection');
|
| 304 |
+
const statusMessagesDiv = document.getElementById('statusMessages');
|
| 305 |
const aiLoader = document.getElementById('aiLoader');
|
| 306 |
+
const loaderProgressBar = document.querySelector('#aiLoader .progress-bar');
|
| 307 |
+
const loaderTextOverlay = document.querySelector('#aiLoader .text-overlay');
|
|
|
|
| 308 |
const outputSection = document.getElementById('outputSection');
|
| 309 |
const btnRestart = document.getElementById('btnRestart');
|
| 310 |
const btnDownloadVideo = document.getElementById('btnDownloadVideo');
|
|
|
|
| 314 |
const TRANSLATOR_SPACE_URL_BASE = "https://hamed744-translate-tts-aloha.hf.space/gradio_api";
|
| 315 |
const FN_INDEX_TRANSLATE_SPEAK = 1;
|
| 316 |
const DEFAULT_TTS_VOICE_TRANSLATOR = "انگلیسی (آمریکا) - جنی (زن)";
|
| 317 |
+
const DEFAULT_NEGATIVE_PROMPT = "bad quality, ugly, blurry, watermark, text, signature";
|
| 318 |
const DEFAULT_PROMPT_TEXT = "";
|
| 319 |
const TRANSPARENT_PIXEL_SRC = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
| 320 |
const DEFAULT_VIDEO_DURATION = 5.1;
|
|
|
|
| 327 |
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15).substring(0, length - 7);
|
| 328 |
}
|
| 329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
async function retryLastAttempt() {
|
| 331 |
if (!lastAttemptedVideoPayload || !lastAttemptedVideoPayload.imageSourceFile || !lastAttemptedVideoPayload.persianPrompt) {
|
| 332 |
showCriticalError("اطلاعات کافی برای تلاش مجدد وجود ندارد. لطفاً از نو شروع کنید.");
|
|
|
|
| 334 |
}
|
| 335 |
|
| 336 |
hideCriticalError();
|
| 337 |
+
clearStatusMessages();
|
| 338 |
generateButton.disabled = true;
|
| 339 |
resultContainer.classList.add('active');
|
| 340 |
statusSection.classList.add('active');
|
| 341 |
+
aiLoader.style.display = 'flex';
|
|
|
|
| 342 |
loaderProgressBar.classList.add('animate-progress');
|
| 343 |
outputSection.classList.remove('active');
|
| 344 |
+
addStatusMessage('شروع تلاش مجدد...', 'info');
|
| 345 |
|
| 346 |
const { persianPrompt, imageSourceFile } = lastAttemptedVideoPayload;
|
| 347 |
|
| 348 |
try {
|
| 349 |
const englishPromptForVideo = await translatePersianToEnglish(persianPrompt);
|
| 350 |
+
addStatusMessage(`ترجمه مجدد: ${englishPromptForVideo}`, 'info');
|
| 351 |
+
|
| 352 |
setAiLoaderText("در حال متحرک سازی تصویر...");
|
| 353 |
|
| 354 |
const client = await Client.connect(VIDEO_SPACE_NAME);
|
|
|
|
| 372 |
}
|
| 373 |
}
|
| 374 |
|
| 375 |
+
function showCriticalError(message, isTranslationError = false) {
|
| 376 |
const lowerCaseMessage = (message || "").toLowerCase();
|
| 377 |
+
|
| 378 |
+
if (lowerCaseMessage.includes("gpu") || lowerCaseMessage.includes("quota") || lowerCaseMessage.includes("exceeded") || lowerCaseMessage.includes("limit") || lowerCaseMessage.includes("capacity") || lowerCaseMessage.includes("queue full")) {
|
| 379 |
+
const detailedGpuErrorHtml = `
|
| 380 |
<div class="gpu-error-message-container">
|
| 381 |
+
<h2><span class="icon">💡</span> راهنمای رفع محدودیت انیمیشن</h2>
|
| 382 |
+
<div class="error-content">
|
| 383 |
+
<strong class="error-message-title">ارور محدودیت استفاده از GPU</strong>
|
| 384 |
+
<p>مدلهای هوش مصنوعی برای اجرا به کارتهای گرافیک بسیار قدرتمند نیاز دارند که این امر گاهی محدودیتهایی جی پی یو را در استفاده از آنها ایجاد میکند. با این حال، خبر خوب این است که این محدودیتها با استفاده از یک ترفند ساده قابل رفع هستند.</p>
|
| 385 |
+
<p><strong>برای رفع محدودیت هر بار اینگونه عمل کنید:</strong></p>
|
| 386 |
+
<ul>
|
| 387 |
+
<li>اگر از اینترنت سیمکارت استفاده میکنید، فیلترشکن خود را حتما خاموش کنید.</li>
|
| 388 |
+
<li>به مدت ۱۰ تا ۱۵ ثانیه، گوشی خود را در حالت هواپیما قرار دهید و سپس آن را غیرفعال کنید. این محدودیت با همین روش برداشته میشود</li>
|
| 389 |
+
<li>اگر از وای فای استفاده میکنید، مودم خود را یک بار خاموش و روشن کنید (بهتر است برای استفاده نامحدود از برنامه از اینترنت سیم کارت استفاده کنید).</li>
|
| 390 |
</ul>
|
| 391 |
+
<p>خلاصه: هربار این صفحه اومد اینگونه عمل کنید از اینترنت سیم کارت استفاده کنید، فیلتر شکن خاموش باشه، یک یا دوبار حالت هواپیما رو روشن و خاموش کنید. تلاش مجدداً بزنید این ارور بر طرف میشه، با این روش میشه بصورت نامحدود با این برنامه انیمیشن ساخت☘️</p>
|
| 392 |
</div>
|
| 393 |
<div class="gpu-error-actions">
|
| 394 |
<button class="action-button back-button" id="gpuErrorGoBackBtn">بازگشت</button>
|
| 395 |
<button class="action-button retry-button" id="gpuErrorRetryBtn">تلاش مجدد</button>
|
| 396 |
</div>
|
| 397 |
</div>`;
|
| 398 |
+
criticalErrorSection.innerHTML = detailedGpuErrorHtml;
|
| 399 |
document.getElementById('gpuErrorGoBackBtn').addEventListener('click', hideCriticalError);
|
| 400 |
document.getElementById('gpuErrorRetryBtn').addEventListener('click', retryLastAttempt);
|
| 401 |
} else {
|
| 402 |
+
criticalErrorSection.innerHTML = `
|
| 403 |
+
<div id="criticalErrorMessage" style="color: var(--danger-color); font-weight: 500; margin-bottom: 1.5rem; line-height: 1.6;"><p>${message}</p></div>
|
| 404 |
<div class="video-controls">
|
| 405 |
<button id="simpleErrorGoBackBtn" class="video-button">بازگشت</button>
|
| 406 |
<button id="simpleErrorRetryBtn" class="video-button primary">تلاش مجدد</button>
|
| 407 |
</div>`;
|
| 408 |
+
document.getElementById('simpleErrorGoBackBtn').addEventListener('click', hideCriticalError);
|
| 409 |
+
document.getElementById('simpleErrorRetryBtn').addEventListener('click', retryLastAttempt);
|
|
|
|
| 410 |
}
|
| 411 |
|
| 412 |
resultContainer.classList.add('active');
|
|
|
|
| 422 |
criticalErrorSection.innerHTML = '';
|
| 423 |
}
|
| 424 |
|
| 425 |
+
function setAiLoaderText(text) {
|
| 426 |
+
if (loaderTextOverlay) loaderTextOverlay.textContent = text;
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
function addStatusMessage(message, type = 'info') {
|
| 430 |
+
if (criticalErrorSection.classList.contains('active') && type === 'error') {
|
| 431 |
+
return;
|
| 432 |
+
}
|
| 433 |
+
resultContainer.classList.add('active');
|
| 434 |
+
statusSection.classList.add('active');
|
| 435 |
+
const messageDiv = document.createElement('div');
|
| 436 |
+
messageDiv.className = `status-message ${type}`;
|
| 437 |
+
const iconSvg = type === 'success'
|
| 438 |
+
? '<svg class="status-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>'
|
| 439 |
+
: type === 'error'
|
| 440 |
+
? '<svg class="status-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>'
|
| 441 |
+
: '<svg class="status-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>';
|
| 442 |
+
messageDiv.innerHTML = `${iconSvg}<span class="status-text">${message}</span>`;
|
| 443 |
+
statusMessagesDiv.insertBefore(messageDiv, statusMessagesDiv.firstChild);
|
| 444 |
+
statusMessagesDiv.scrollTop = 0;
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
function clearStatusMessages() {
|
| 448 |
+
statusMessagesDiv.innerHTML = '';
|
| 449 |
+
if(loaderProgressBar) {
|
| 450 |
+
loaderProgressBar.classList.remove('animate-progress');
|
| 451 |
+
loaderProgressBar.style.width = '0%';
|
| 452 |
+
}
|
| 453 |
+
aiLoader.style.display = 'none';
|
| 454 |
+
if (outputVideo.src && outputVideo.src.startsWith('blob:')) {
|
| 455 |
+
URL.revokeObjectURL(outputVideo.src);
|
| 456 |
+
}
|
| 457 |
+
outputVideo.src = '';
|
| 458 |
+
finalSeedElement.textContent = '';
|
| 459 |
+
btnDownloadVideo.style.display = 'none';
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
function initializeForm() {
|
| 463 |
hideCriticalError();
|
| 464 |
resultContainer.classList.remove('active');
|
| 465 |
statusSection.classList.remove('active');
|
| 466 |
outputSection.classList.remove('active');
|
| 467 |
+
clearStatusMessages();
|
| 468 |
lastAttemptedVideoPayload = null;
|
| 469 |
imageFileInput.value = '';
|
| 470 |
imagePreview.src = TRANSPARENT_PIXEL_SRC;
|
|
|
|
| 474 |
}
|
| 475 |
|
| 476 |
async function translatePersianToEnglish(persianText) {
|
|
|
|
| 477 |
let currentTranslatorSessionHash = generateRandomHash();
|
| 478 |
+
const payload = {
|
| 479 |
+
data: [persianText, DEFAULT_TTS_VOICE_TRANSLATOR, 0, 0, 0],
|
| 480 |
+
event_data: null,
|
| 481 |
+
fn_index: FN_INDEX_TRANSLATE_SPEAK,
|
| 482 |
+
session_hash: currentTranslatorSessionHash
|
| 483 |
+
};
|
| 484 |
+
|
| 485 |
+
if (aiLoader.style.display !== 'none') {
|
| 486 |
+
setAiLoaderText("در حال ترجمه دستور شما...");
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
try {
|
| 490 |
+
const joinResponse = await fetch(`${TRANSLATOR_SPACE_URL_BASE}/queue/join?`, {
|
| 491 |
+
method: 'POST',
|
| 492 |
+
headers: { 'Content-Type': 'application/json' },
|
| 493 |
+
body: JSON.stringify(payload)
|
| 494 |
+
});
|
| 495 |
+
if (!joinResponse.ok) throw new Error(`خطای اتصال به سرور مترجم: ${joinResponse.status}`);
|
| 496 |
+
const joinData = await joinResponse.json();
|
| 497 |
+
if (!joinData.event_id) throw new Error("پاسخ نامعتبر از سرور مترجم.");
|
| 498 |
+
|
| 499 |
return new Promise((resolve, reject) => {
|
| 500 |
const eventSource = new EventSource(`${TRANSLATOR_SPACE_URL_BASE}/queue/data?session_hash=${currentTranslatorSessionHash}`);
|
| 501 |
+
eventSource.onmessage = function(event) {
|
| 502 |
const data = JSON.parse(event.data);
|
| 503 |
if (data.msg === "process_completed") {
|
| 504 |
eventSource.close();
|
| 505 |
+
if (data.success && data.output?.data?.[0]) {
|
| 506 |
+
resolve(data.output.data[0]);
|
| 507 |
+
} else {
|
| 508 |
+
reject(new Error(data.output?.error || "ترجمه ناموفق بود."));
|
| 509 |
+
}
|
| 510 |
+
} else if (data.msg === "process_starts") {
|
| 511 |
+
if (aiLoader.style.display !== 'none') setAiLoaderText("ترجمه در سرور آغاز شد...");
|
| 512 |
}
|
| 513 |
};
|
| 514 |
+
eventSource.onerror = (err) => {
|
| 515 |
+
eventSource.close();
|
| 516 |
+
reject(new Error("خطا در ارتباط با سرور مترجم."));
|
| 517 |
+
};
|
| 518 |
});
|
| 519 |
+
} catch (error) {
|
| 520 |
+
console.error("خطای کلی در ترجمه:", error);
|
| 521 |
+
throw error;
|
| 522 |
+
}
|
| 523 |
}
|
| 524 |
|
| 525 |
function handleSuccessfulVideoGeneration(result) {
|
|
|
|
| 528 |
showCriticalError('پاسخ دریافتی از سرور انیمیشن معتبر نیست.');
|
| 529 |
return;
|
| 530 |
}
|
| 531 |
+
|
| 532 |
+
addStatusMessage('انیمیشن شما آماده شد! 🎉', 'success');
|
| 533 |
const videoUrl = result.data[0].video.url;
|
| 534 |
const usedSeed = result.data[1];
|
| 535 |
|
| 536 |
+
setTimeout(() => {
|
| 537 |
+
statusSection.classList.remove('active');
|
| 538 |
+
outputSection.classList.add('active');
|
| 539 |
+
outputVideo.src = videoUrl;
|
| 540 |
+
outputVideo.load();
|
| 541 |
+
finalSeedElement.textContent = `Seed: ${usedSeed}`;
|
| 542 |
+
prepareCustomDownloadLink(videoUrl);
|
| 543 |
+
}, 300);
|
| 544 |
|
| 545 |
generateButton.disabled = false;
|
| 546 |
}
|
|
|
|
| 560 |
|
| 561 |
// Event Listeners
|
| 562 |
document.addEventListener('DOMContentLoaded', initializeForm);
|
| 563 |
+
|
| 564 |
+
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { imageDropZone.addEventListener(eventName, ev => { ev.preventDefault(); ev.stopPropagation(); }); });
|
| 565 |
+
['dragenter', 'dragover'].forEach(eventName => { imageDropZone.addEventListener(eventName, () => { if (!imageDropZone.classList.contains('has-image')) { imageDropZone.classList.add('drag-over'); } }); });
|
| 566 |
+
['dragleave', 'drop'].forEach(eventName => { imageDropZone.addEventListener(eventName, () => { imageDropZone.classList.remove('drag-over'); }); });
|
|
|
|
| 567 |
imageDropZone.addEventListener('drop', e => {
|
| 568 |
+
if (!imageDropZone.classList.contains('has-image') && e.dataTransfer.files.length > 0) {
|
| 569 |
imageFileInput.files = e.dataTransfer.files;
|
| 570 |
+
const changeEvent = new Event('change', { bubbles: true });
|
| 571 |
+
imageFileInput.dispatchEvent(changeEvent);
|
| 572 |
}
|
| 573 |
});
|
| 574 |
+
|
| 575 |
+
btnRestart.addEventListener('click', initializeForm);
|
| 576 |
|
| 577 |
imageFileInput.addEventListener('change', function(event) {
|
| 578 |
const file = event.target.files[0];
|
| 579 |
hideCriticalError();
|
| 580 |
if (file) {
|
| 581 |
if (!file.type.startsWith('image/')) {
|
| 582 |
+
addStatusMessage('فرمت نامعتبر', 'error');
|
| 583 |
imageFileInput.value = '';
|
| 584 |
+
imagePreview.src = TRANSPARENT_PIXEL_SRC;
|
| 585 |
+
imageDropZone.classList.remove('has-image');
|
| 586 |
} else {
|
| 587 |
const reader = new FileReader();
|
| 588 |
reader.onload = (e) => {
|
|
|
|
| 601 |
const persianPrompt = promptInput.value.trim();
|
| 602 |
const imageFile = imageFileInput.files[0];
|
| 603 |
|
| 604 |
+
if (!persianPrompt) {
|
| 605 |
+
addStatusMessage('لطفاً دستور حرکت را وارد کنید.', 'error');
|
| 606 |
+
promptInput.focus();
|
| 607 |
+
return;
|
| 608 |
+
}
|
| 609 |
+
if (!imageFile) {
|
| 610 |
+
addStatusMessage('لطفاً تصویر انتخاب کنید.', 'error');
|
| 611 |
+
return;
|
| 612 |
+
}
|
| 613 |
|
| 614 |
+
clearStatusMessages();
|
| 615 |
hideCriticalError();
|
| 616 |
generateButton.disabled = true;
|
| 617 |
resultContainer.classList.add('active');
|
| 618 |
statusSection.classList.add('active');
|
| 619 |
+
aiLoader.style.display = 'flex';
|
|
|
|
| 620 |
loaderProgressBar.classList.add('animate-progress');
|
| 621 |
outputSection.classList.remove('active');
|
| 622 |
+
addStatusMessage('در حال آماده سازی...', 'info');
|
| 623 |
|
| 624 |
+
lastAttemptedVideoPayload = {
|
| 625 |
+
persianPrompt: persianPrompt,
|
| 626 |
+
imageSourceFile: imageFile
|
| 627 |
+
};
|
| 628 |
|
| 629 |
try {
|
| 630 |
const englishPrompt = await translatePersianToEnglish(persianPrompt);
|
| 631 |
+
addStatusMessage(`ترجمه: ${englishPrompt}`, 'info');
|
| 632 |
+
|
| 633 |
setAiLoaderText("در حال متحرک سازی تصویر...");
|
| 634 |
|
| 635 |
const client = await Client.connect(VIDEO_SPACE_NAME);
|
|
|
|
| 648 |
handleSuccessfulVideoGeneration(result);
|
| 649 |
|
| 650 |
} catch (errorCaught) {
|
| 651 |
+
const isTranslationErr = errorCaught.message.toLowerCase().includes("مترجم");
|
| 652 |
+
showCriticalError(`خطا: ${errorCaught.message}`, isTranslationErr);
|
| 653 |
}
|
| 654 |
});
|
| 655 |
})();
|
| 656 |
</script>
|
| 657 |
+
|
| 658 |
+
<script>
|
| 659 |
+
// --- Header Animation Script ---
|
| 660 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 661 |
+
const canvas = document.getElementById('neural-network-canvas');
|
| 662 |
+
if (!canvas) return;
|
| 663 |
+
const header = canvas.parentElement;
|
| 664 |
+
const ctx = canvas.getContext('2d');
|
| 665 |
+
let particles = [];
|
| 666 |
+
const particleCount = 20; const maxDistance = 100;
|
| 667 |
+
const computedStyles = getComputedStyle(document.documentElement);
|
| 668 |
+
const particleColor = computedStyles.getPropertyValue('--accent-primary').trim();
|
| 669 |
+
const lineColor = computedStyles.getPropertyValue('--text-tertiary').trim();
|
| 670 |
+
function resizeCanvas() { canvas.width = header.clientWidth; canvas.height = header.clientHeight; init(); }
|
| 671 |
+
class Particle { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = (Math.random() - 0.5) * 0.3; this.vy = (Math.random() - 0.5) * 0.3; this.radius = 1.2; } update() { this.x += this.vx; this.y += this.vy; if (this.x < 0 || this.x > canvas.width) this.vx *= -1; if (this.y < 0 || this.y > canvas.height) this.vy *= -1; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = particleColor; ctx.fill(); } }
|
| 672 |
+
function init() { particles = []; for (let i = 0; i < particleCount; i++) { particles.push(new Particle()); } }
|
| 673 |
+
function connectParticles() { for (let i = 0; i < particles.length; i++) { for (let j = i + 1; j < particles.length; j++) { const dx = particles[i].x - particles[j].x; const dy = particles[i].y - particles[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < maxDistance) { ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = lineColor; ctx.lineWidth = 0.2; ctx.globalAlpha = 1 - distance / maxDistance; ctx.stroke(); } } } ctx.globalAlpha = 1; }
|
| 674 |
+
function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(particle => { particle.update(); particle.draw(); }); connectParticles(); requestAnimationFrame(animate); }
|
| 675 |
+
window.addEventListener('resize', resizeCanvas); resizeCanvas(); animate();
|
| 676 |
+
});
|
| 677 |
+
</script>
|
| 678 |
</body>
|
| 679 |
</html>
|