Hamed744 commited on
Commit
b7f33b9
·
verified ·
1 Parent(s): d545782

Create Yek.html

Browse files
Files changed (1) hide show
  1. Yek.html +660 -0
Yek.html ADDED
@@ -0,0 +1,660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>&nbsp;</p>
2
+ <style>
3
+ :root {
4
+ --color-primary: #667EEA; /* بنفش آبی ملایم */
5
+ --color-secondary: #764BA2; /* بنفش تیره‌تر */
6
+ --color-text: #2D3748; /* متن اصلی تیره */
7
+ --color-text-muted: #718096; /* متن کم‌رنگ‌تر */
8
+ -- rozmowy: #F7FAFC; /* پس‌زمینه روشن برای بخش‌ها */
9
+ --color-border: #E2E8F0;
10
+ --color-white: #FFFFFF;
11
+ --color-success: #48BB78;
12
+ --color-error: #F56565;
13
+ --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
14
+ --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
15
+ --border-radius-md: 10px;
16
+ --border-radius-lg: 16px;
17
+ }
18
+
19
+ * { box-sizing: border-box; margin: 0; padding: 0; }
20
+
21
+ body {
22
+ background-color: var(--color-white); /* پس‌زمینه ساده سفید */
23
+ font-family: 'Vazirmatn', sans-serif;
24
+ color: var(--color-text);
25
+ line-height: 1.6;
26
+ }
27
+
28
+ .ltx-video-creator-app {
29
+ width: 100%;
30
+ max-width: 500px;
31
+ margin: 20px auto;
32
+ padding: 0 10px;
33
+ }
34
+
35
+ .app-header {
36
+ text-align: center;
37
+ margin-bottom: 25px;
38
+ }
39
+ .app-header h1 {
40
+ font-size: 1.9em;
41
+ font-weight: 700;
42
+ background: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
43
+ -webkit-background-clip: text;
44
+ -webkit-text-fill-color: transparent;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ margin-bottom: 8px;
49
+ }
50
+ .app-header h1 img {
51
+ width: 32px;
52
+ height: 32px;
53
+ margin-left: 10px;
54
+ }
55
+ .app-header p {
56
+ font-size: 0.95em;
57
+ color: var(--color-text-muted);
58
+ }
59
+
60
+ .form-section-card {
61
+ background-color: var(--color-white);
62
+ padding: 25px;
63
+ border-radius: var(--border-radius-lg);
64
+ box-shadow: var(--shadow-lg);
65
+ border: 1px solid var(--color-border);
66
+ }
67
+
68
+ .mode-selector {
69
+ display: flex;
70
+ background-color: var(--color-bg-light);
71
+ border-radius: var(--border-radius-md);
72
+ padding: 4px;
73
+ margin-bottom: 20px;
74
+ border: 1px solid var(--color-border);
75
+ }
76
+ .mode-button {
77
+ flex: 1;
78
+ padding: 8px 10px;
79
+ background-color: transparent;
80
+ border: none;
81
+ cursor: pointer;
82
+ font-family: inherit;
83
+ font-size: 0.85rem;
84
+ font-weight: 600;
85
+ color: var(--color-text-muted);
86
+ border-radius: 8px;
87
+ transition: all 0.25s ease;
88
+ }
89
+ .mode-button.active {
90
+ background-color: var(--color-primary);
91
+ color: var(--color-white);
92
+ box-shadow: 0 3px 8px rgba(102, 126, 234, 0.3);
93
+ }
94
+
95
+ .form-group {
96
+ margin-bottom: 18px;
97
+ }
98
+ label {
99
+ display: block;
100
+ font-size: 0.8rem;
101
+ text-transform: uppercase;
102
+ letter-spacing: 0.5px;
103
+ color: var(--color-text-muted);
104
+ margin-bottom: 6px;
105
+ font-weight: 600;
106
+ }
107
+ input[type="text"], textarea, input[type="number"] {
108
+ width: 100%;
109
+ padding: 11px 14px;
110
+ border: 1px solid var(--color-border);
111
+ border-radius: var(--border-radius-md);
112
+ font-size: 0.9rem;
113
+ background-color: var(--color-bg-light);
114
+ color: var(--color-text);
115
+ font-family: inherit;
116
+ }
117
+ input[type="text"]::placeholder, textarea::placeholder {
118
+ color: #A0AEC0;
119
+ }
120
+
121
+ input[type="file"] {
122
+ width: 100%;
123
+ padding: 10px;
124
+ border: 1px solid var(--color-border);
125
+ border-radius: var(--border-radius-md);
126
+ font-size: 0.9em;
127
+ background-color: var(--color-bg-light);
128
+ font-family: inherit;
129
+ }
130
+ input[type="file"]::file-selector-button {
131
+ padding: 8px 15px;
132
+ margin-right: 10px;
133
+ background-color: #6c757d;
134
+ color: white;
135
+ border: none;
136
+ border-radius: 7px;
137
+ cursor: pointer;
138
+ font-family: inherit;
139
+ font-weight: 500;
140
+ }
141
+ input[type="file"]::file-selector-button:hover {
142
+ background-color: #5a6268;
143
+ }
144
+
145
+ input:focus, textarea:focus {
146
+ outline: none;
147
+ border-color: var(--color-primary);
148
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.25);
149
+ background-color: var(--color-white);
150
+ }
151
+ textarea {
152
+ min-height: 60px;
153
+ resize: vertical;
154
+ }
155
+ .preview-image {
156
+ max-width: 100%;
157
+ max-height: 150px;
158
+ border-radius: var(--border-radius-md);
159
+ margin-top: 12px;
160
+ display: none;
161
+ border: 1px solid var(--color-border);
162
+ object-fit: cover;
163
+ box-shadow: var(--shadow-md);
164
+ }
165
+
166
+ .options-grid {
167
+ display: grid;
168
+ grid-template-columns: 1fr 1fr;
169
+ gap: 10px;
170
+ }
171
+ .option-button {
172
+ padding: 10px;
173
+ font-size: 0.85em;
174
+ font-weight: 500;
175
+ border: 1px solid var(--color-border);
176
+ border-radius: var(--border-radius-md);
177
+ background-color: var(--color-bg-light);
178
+ color: var(--color-text);
179
+ cursor: pointer;
180
+ text-align: center;
181
+ font-family: inherit;
182
+ }
183
+ .option-button.selected {
184
+ background-color: var(--color-primary);
185
+ color: var(--color-white);
186
+ border-color: var(--color-primary);
187
+ font-weight: 600;
188
+ }
189
+
190
+ .flex-row {
191
+ display: flex;
192
+ gap: 10px;
193
+ }
194
+ .flex-row .form-group {
195
+ flex: 1;
196
+ }
197
+
198
+ .button-container {
199
+ text-align: center;
200
+ margin-top: 25px;
201
+ }
202
+ #generateButton {
203
+ background: linear-gradient(45deg, var(--color-primary) 0%, var(--color-secondary) 100%);
204
+ color: white;
205
+ padding: 13px 28px;
206
+ border: none;
207
+ border-radius: var(--border-radius-md);
208
+ font-size: 1em;
209
+ font-weight: 600;
210
+ cursor: pointer;
211
+ font-family: inherit;
212
+ width: 100%;
213
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
214
+ }
215
+ #generateButton:hover:not(:disabled) {
216
+ transform: scale(1.02);
217
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
218
+ }
219
+ #generateButton:disabled {
220
+ background: var(--color-border);
221
+ color: var(--color-text-muted);
222
+ cursor: not-allowed;
223
+ box-shadow: none;
224
+ }
225
+
226
+ .status-section, .output-section {
227
+ margin-top: 25px;
228
+ padding: 20px;
229
+ background-color: var(--color-white);
230
+ border-radius: var(--border-radius-lg);
231
+ border: 1px solid var(--color-border);
232
+ box-shadow: var(--shadow-md);
233
+ }
234
+ .status-section h2, .output-section h2 {
235
+ font-size: 1.15em;
236
+ color: var(--color-text);
237
+ border-bottom: 1px solid var(--color-border);
238
+ padding-bottom: 8px;
239
+ margin-bottom: 15px;
240
+ font-weight: 600;
241
+ }
242
+ .loader {
243
+ width: 30px;
244
+ height: 30px;
245
+ margin: 15px auto;
246
+ display: none;
247
+ border-radius: 50%;
248
+ border: 3px solid var(--color-bg-light);
249
+ border-top-color: var(--color-primary);
250
+ animation: spin 0.7s linear infinite;
251
+ }
252
+ @keyframes spin { to { transform: rotate(360deg); } }
253
+
254
+ .progress-bar-container {
255
+ width: 100%;
256
+ background-color: var(--color-border);
257
+ border-radius: 6px;
258
+ margin: 12px 0;
259
+ display: none;
260
+ height: 6px;
261
+ }
262
+ .progress-bar {
263
+ width: 0%;
264
+ height: 100%;
265
+ background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-secondary) 100%);
266
+ border-radius: 6px;
267
+ }
268
+ .status-messages-container {
269
+ max-height: 90px;
270
+ overflow-y: auto;
271
+ padding-right: 5px;
272
+ }
273
+ .status-message {
274
+ padding: 8px 12px;
275
+ border-radius: var(--border-radius-md);
276
+ margin-top: 8px;
277
+ font-size: 0.85em;
278
+ background-color: var(--color-bg-light);
279
+ }
280
+ .status-message.info {
281
+ border-left: 3px solid var(--color-primary);
282
+ background-color: #EBF4FF;
283
+ color: #0D47A1;
284
+ }
285
+ .status-message.error {
286
+ border-left: 3px solid var(--color-error);
287
+ background-color: #FFEBEE;
288
+ color: #C62828;
289
+ }
290
+ .status-message.success {
291
+ border-left: 3px solid var(--color-success);
292
+ background-color: #E8F5E9;
293
+ color: #1B5E20;
294
+ }
295
+
296
+ #outputVideo {
297
+ width: 100%;
298
+ border-radius: var(--border-radius-md);
299
+ margin-top: 10px;
300
+ border: 1px solid var(--color-border);
301
+ background-color: #000;
302
+ }
303
+ #finalSeed {
304
+ font-size: 0.8em;
305
+ color: var(--color-text-muted);
306
+ text-align: center;
307
+ margin-top: 8px;
308
+ }
309
+
310
+ .hidden-section {
311
+ display: none !important;
312
+ }
313
+
314
+ @media (max-width: 480px) {
315
+ .ltx-video-creator-app {
316
+ padding: 0;
317
+ margin: 10px auto;
318
+ }
319
+ .app-header h1 {
320
+ font-size: 1.6em;
321
+ }
322
+ .form-section-card {
323
+ padding: 15px;
324
+ border-radius: var(--border-radius-md);
325
+ }
326
+ #generateButton {
327
+ padding: 12px 18px;
328
+ font-size: 0.95em;
329
+ }
330
+ }
331
+ </style>
332
+ <div class="ltx-video-creator-app">
333
+ <div class="app-header">
334
+ <h1><img src="https://em-content.zobj.net/thumbs/120/apple/354/film-projector_1f4fd-fe0f.png" alt="🎬" /> ویدیو ساز LTX</h1>
335
+ <p>انیمیشن&zwnj;های کوتاه و جذاب خلق کنید</p>
336
+ </div>
337
+ <div class="form-section-card">
338
+ <div class="mode-selector"><button class="mode-button active" data-mode="image-to-video">از تصویر</button> <button class="mode-button" data-mode="text-to-video">از متن</button></div>
339
+ <div id="imageToVideoSection" class="form-mode-section">
340
+ <div class="form-group"><label for="imageFile">تصویر شما</label> <input type="file" id="imageFile" accept="image/jpeg, image/png, image/webp" /> <img id="imagePreview" src="#" alt="پیش&zwnj;نمایش" class="preview-image" /></div>
341
+ </div>
342
+ <div id="textToVideoSection" class="form-mode-section hidden-section">
343
+ <p style="text-align: center; font-size: 0.8rem; margin-bottom: 15px; padding: 8px; background-color: var(--color-bg-light); border-radius: 7px;">در این حالت، ویدیو تنها بر اساس شرح شما ساخته می&zwnj;شود.</p>
344
+ </div>
345
+ <div class="form-group"><label for="prompt">شرح انیمیشن (Prompt)</label> <textarea id="prompt" rows="2" placeholder="مثال: گربه&zwnj;ای که در چمنزار می&zwnj;دود">یک موجود از داخل تصویر شروع به حرکت می‌کند</textarea></div>
346
+ <div class="form-group"><label>مدت زمان</label>
347
+ <div class="options-grid" id="durationButtonsContainer"><button class="option-button duration-button" data-api-duration="5">کوتاه (۵ ثانیه)</button> <button class="option-button duration-button selected" data-api-duration="7.8">استاندارد (۸ ثانیه)</button></div>
348
+ </div>
349
+ <div class="form-group"><label>نسبت تصویر خروجی</label>
350
+ <div class="options-grid" id="aspectRatioButtonsContainer"><button class="option-button aspect-ratio-button selected" data-height="768" data-width="768">۱:۱ (مربع)</button> <button class="option-button aspect-ratio-button" data-height="768" data-width="432">۹:۱۶ (پرتره)</button> <button class="option-button aspect-ratio-button" data-height="432" data-width="768">۱۶:۹ (لنداسکیپ)</button> <button class="option-button aspect-ratio-button" data-height="512" data-width="704">LTX (۵۱۲&times;۷۰۴)</button></div>
351
+ </div>
352
+ <div class="form-group"><label for="negativePrompt">موارد ناخواسته (Negative Prompt)</label> <textarea id="negativePrompt" rows="2" placeholder="مثال: کیفیت پایین, متن">کیفیت پایین، تار، لرزان, متن ناخوانا</textarea></div>
353
+ <div class="flex-row">
354
+ <div class="form-group"><label for="cfgScale">قدرت Prompt</label> <input type="number" id="cfgScale" value="1.0" min="1.0" max="10.0" step="0.1" /></div>
355
+ <div class="form-group"><label for="seed">سید (0=تصادفی)</label> <input type="number" id="seed" value="0" min="0" /></div>
356
+ </div>
357
+ <div class="flex-row">
358
+ <div class="form-group"><label for="outputHeight">ارتفاع (px)</label> <input type="number" id="outputHeight" value="768" step="32" min="256" max="1280" /></div>
359
+ <div class="form-group"><label for="outputWidth">عرض (px)</label> <input type="number" id="outputWidth" value="768" step="32" min="256" max="1280" /></div>
360
+ </div>
361
+ <div class="button-container"><button id="generateButton">🚀 تولید ویدیو</button></div>
362
+ </div>
363
+ <div class="status-section" id="statusSection" style="display: none;">
364
+ <h2>وضعیت پردازش</h2>
365
+ <div class="loader" id="loader"></div>
366
+ <div class="progress-bar-container" id="progressBarContainer">
367
+ <div class="progress-bar" id="progressBar"></div>
368
+ </div>
369
+ <div class="status-messages-container" id="statusMessages"></div>
370
+ </div>
371
+ <div class="output-section" id="outputSection" style="display: none;">
372
+ <h2>ویدیوی شما آماده است!</h2>
373
+ <video width="300" height="150" id="outputVideo" controls="controls" preload="metadata" playsinline=""></video>
374
+ <p id="finalSeed">&nbsp;</p>
375
+ </div>
376
+ </div>
377
+ <p>
378
+ <script>
379
+ const imageFileInput = document.getElementById('imageFile');
380
+ const imagePreview = document.getElementById('imagePreview');
381
+ const promptInput = document.getElementById('prompt');
382
+ const durationButtons = document.querySelectorAll('.duration-button');
383
+ const aspectRatioButtons = document.querySelectorAll('.aspect-ratio-button');
384
+ const generateButton = document.getElementById('generateButton');
385
+ const outputVideo = document.getElementById('outputVideo');
386
+ const finalSeedElement = document.getElementById('finalSeed');
387
+ const statusMessagesDiv = document.getElementById('statusMessages');
388
+ const statusSection = document.getElementById('statusSection');
389
+ const outputSection = document.getElementById('outputSection');
390
+ const loader = document.getElementById('loader');
391
+ const progressBarContainer = document.getElementById('progressBarContainer');
392
+ const progressBar = document.getElementById('progressBar');
393
+ const negativePromptInput = document.getElementById('negativePrompt');
394
+ const cfgScaleInput = document.getElementById('cfgScale');
395
+ const seedInput = document.getElementById('seed');
396
+ const outputHeightInput = document.getElementById('outputHeight');
397
+ const outputWidthInput = document.getElementById('outputWidth');
398
+ const modeButtons = document.querySelectorAll('.mode-button');
399
+ const imageToVideoSection = document.getElementById('imageToVideoSection');
400
+ const textToVideoSection = document.getElementById('textToVideoSection');
401
+ let currentGenerationMode = "image-to-video";
402
+ const SPACE_URL_BASE = "https://lightricks-ltx-video-distilled.hf.space";
403
+ let selectedApiDuration = 7.8;
404
+ let currentSessionHash = '';
405
+ let uploadedImageInfo = null;
406
+
407
+ document.addEventListener('DOMContentLoaded', () => {
408
+ document.querySelector('.duration-button.selected')?.click();
409
+ document.querySelector('.aspect-ratio-button.selected')?.click();
410
+ updateFormForMode(currentGenerationMode);
411
+ });
412
+
413
+ modeButtons.forEach(button => {
414
+ button.addEventListener('click', () => {
415
+ modeButtons.forEach(btn => btn.classList.remove('active'));
416
+ button.classList.add('active');
417
+ currentGenerationMode = button.dataset.mode;
418
+ updateFormForMode(currentGenerationMode);
419
+ });
420
+ });
421
+
422
+ function updateFormForMode(mode) {
423
+ if (mode === "image-to-video") {
424
+ imageToVideoSection.classList.remove('hidden-section');
425
+ textToVideoSection.classList.add('hidden-section');
426
+ promptInput.placeholder = "مثال: گربه‌ای در چمنزار آفتابی می‌دود";
427
+ } else {
428
+ imageToVideoSection.classList.add('hidden-section');
429
+ textToVideoSection.classList.remove('hidden-section');
430
+ imageFileInput.value = '';
431
+ imagePreview.style.display = 'none';
432
+ uploadedImageInfo = null;
433
+ promptInput.placeholder = "مثال: یک اژدهای آتشین بر فراز قلعه‌ای قرون وسطایی";
434
+ }
435
+ }
436
+
437
+ imageFileInput.addEventListener('change', function(event) {
438
+ const file = event.target.files[0];
439
+ if (file) {
440
+ if (!file.type.startsWith('image/')) {
441
+ addStatusMessage('لطفاً یک فایل تصویری انتخاب کنید.', 'error');
442
+ imageFileInput.value = '';
443
+ imagePreview.style.display = 'none';
444
+ return;
445
+ }
446
+ const reader = new FileReader();
447
+ reader.onload = function(e) {
448
+ imagePreview.src = e.target.result;
449
+ imagePreview.style.display = 'block';
450
+ };
451
+ reader.readAsDataURL(file);
452
+ uploadedImageInfo = null;
453
+ } else {
454
+ imagePreview.style.display = 'none';
455
+ }
456
+ });
457
+
458
+ durationButtons.forEach(button => {
459
+ button.addEventListener('click', () => {
460
+ durationButtons.forEach(btn => btn.classList.remove('selected'));
461
+ button.classList.add('selected');
462
+ selectedApiDuration = parseFloat(button.dataset.apiDuration);
463
+ });
464
+ });
465
+
466
+ aspectRatioButtons.forEach(button => {
467
+ button.addEventListener('click', () => {
468
+ aspectRatioButtons.forEach(btn => btn.classList.remove('selected'));
469
+ button.classList.add('selected');
470
+ outputHeightInput.value = button.dataset.height;
471
+ outputWidthInput.value = button.dataset.width;
472
+ });
473
+ });
474
+
475
+ function addStatusMessage(message, type = 'info') {
476
+ statusSection.style.display = 'block';
477
+ const messageDiv = document.createElement('div');
478
+ messageDiv.className = `status-message ${type}`;
479
+ messageDiv.textContent = message;
480
+ statusMessagesDiv.insertBefore(messageDiv, statusMessagesDiv.firstChild);
481
+ }
482
+
483
+ function clearStatusAndOutput() {
484
+ statusMessagesDiv.innerHTML = '';
485
+ statusSection.style.display = 'none';
486
+ outputSection.style.display = 'none';
487
+ if (outputVideo.src) URL.revokeObjectURL(outputVideo.src);
488
+ outputVideo.src = '';
489
+ finalSeedElement.textContent = '';
490
+ progressBar.style.width = '0%';
491
+ progressBarContainer.style.display = 'none';
492
+ }
493
+
494
+ function generateRandomHash(length = 11) {
495
+ const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
496
+ let result = '';
497
+ for (let i = 0; i < length; i++) {
498
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
499
+ }
500
+ return result;
501
+ }
502
+
503
+ async function uploadImage(file) {
504
+ addStatusMessage('۱. آماده‌سازی تصویر...');
505
+ const uploadId = generateRandomHash(12);
506
+ const formData = new FormData();
507
+ formData.append('files', file);
508
+ try {
509
+ const response = await fetch(`${SPACE_URL_BASE}/gradio_api/upload?upload_id=${uploadId}`, {
510
+ method: 'POST',
511
+ body: formData
512
+ });
513
+ if (!response.ok) throw new Error(`خطا در آپلود (${response.status})`);
514
+ const result = await response.json();
515
+ if (result && result.length > 0 && typeof result[0] === 'string') {
516
+ return {
517
+ path: result[0],
518
+ url: `${SPACE_URL_BASE}/gradio_api/file=${result[0]}`,
519
+ orig_name: file.name,
520
+ size: file.size,
521
+ mime_type: file.type || 'application/octet-stream',
522
+ meta: { "_type": "gradio.FileData" }
523
+ };
524
+ }
525
+ throw new Error('پاسخ آپلود نامعتبر.');
526
+ } catch (error) {
527
+ addStatusMessage(`خطا در آپلود: ${error.message}`, 'error');
528
+ return null;
529
+ }
530
+ }
531
+
532
+ async function submitToQueue(payload) {
533
+ addStatusMessage('۲. ارسال درخواست به سرور...');
534
+ try {
535
+ const response = await fetch(`${SPACE_URL_BASE}/gradio_api/queue/join`, {
536
+ method: 'POST',
537
+ headers: { 'Content-Type': 'application/json' },
538
+ body: JSON.stringify(payload)
539
+ });
540
+ if (!response.ok) throw new Error(`خطا در ارسال به صف (${response.status})`);
541
+ return await response.json();
542
+ } catch (error) {
543
+ addStatusMessage(`خطا در ارتباط با سرور: ${error.message}`, 'error');
544
+ return null;
545
+ }
546
+ }
547
+
548
+ function listenForResults(eventId) {
549
+ addStatusMessage('۳. در حال تولید ویدیو...');
550
+ const eventSource = new EventSource(`${SPACE_URL_BASE}/gradio_api/queue/data?session_hash=${currentSessionHash}`);
551
+
552
+ eventSource.onmessage = function(event) {
553
+ const data = JSON.parse(event.data);
554
+ if (data.event_id !== eventId && data.msg !== "queue_full") return;
555
+ switch (data.msg) {
556
+ case "process_starts":
557
+ addStatusMessage('عملیات در سرور آغاز شد.');
558
+ progressBarContainer.style.display = 'block';
559
+ progressBar.style.width = '0%';
560
+ break;
561
+ case "progress":
562
+ if (data.progress_data && data.progress_data.length > 0) {
563
+ const progress = Math.round((data.progress_data[0].progress || 0) * 100);
564
+ progressBar.style.width = `${progress}%`;
565
+ }
566
+ break;
567
+ case "process_completed":
568
+ eventSource.close();
569
+ loader.style.display = 'none';
570
+ generateButton.disabled = false;
571
+ progressBar.style.width = '100%';
572
+ if (data.success && data.output?.data?.[0]?.video?.url) {
573
+ addStatusMessage('ویدیو با موفقیت ساخته شد! 🎉', 'success');
574
+ outputSection.style.display = 'block';
575
+ outputVideo.src = data.output.data[0].video.url;
576
+ outputVideo.load();
577
+ finalSeedElement.textContent = data.output.data[1] ? `سید نهایی: ${data.output.data[1]}` : '';
578
+ } else {
579
+ addStatusMessage('تولید ویدیو ناموفق بود.', 'error');
580
+ }
581
+ break;
582
+ case "queue_full":
583
+ addStatusMessage('سرور مشغول است، لطفاً کمی بعد تلاش کنید.', 'error');
584
+ eventSource.close();
585
+ loader.style.display = 'none';
586
+ generateButton.disabled = false;
587
+ break;
588
+ }
589
+ };
590
+
591
+ eventSource.onerror = function() {
592
+ addStatusMessage('خطا در ارتباط با سرور.', 'error');
593
+ eventSource.close();
594
+ loader.style.display = 'none';
595
+ generateButton.disabled = false;
596
+ };
597
+ }
598
+
599
+ generateButton.addEventListener('click', async () => {
600
+ clearStatusAndOutput();
601
+ const imageFile = imageFileInput.files[0];
602
+
603
+ if (currentGenerationMode === "image-to-video" && !imageFile) {
604
+ addStatusMessage('لطفاً ابتدا یک تصویر انتخاب کنید.', 'error');
605
+ return;
606
+ }
607
+ if (!promptInput.value.trim()) {
608
+ addStatusMessage('لطفاً متن راهنما (Prompt) را وارد کنید.', 'error');
609
+ return;
610
+ }
611
+
612
+ generateButton.disabled = true;
613
+ loader.style.display = 'block';
614
+ current
615
+
616
+ SessionHash = generateRandomHash();
617
+
618
+ if (currentGenerationMode === "image-to-video") {
619
+ uploadedImageInfo = await uploadImage(imageFile);
620
+ if (!uploadedImageInfo) {
621
+ generateButton.disabled = false;
622
+ loader.style.display = 'none';
623
+ return;
624
+ }
625
+ } else {
626
+ uploadedImageInfo = null;
627
+ }
628
+
629
+ const userSeed = parseInt(seedInput.value);
630
+ const generationPayload = {
631
+ fn_index: 5,
632
+ data: [
633
+ promptInput.value,
634
+ negativePromptInput.value,
635
+ uploadedImageInfo,
636
+ (currentGenerationMode === "image-to-video" ? null : ""),
637
+ parseInt(outputHeightInput.value),
638
+ parseInt(outputWidthInput.value),
639
+ currentGenerationMode,
640
+ selectedApiDuration,
641
+ 9,
642
+ (userSeed > 0) ? userSeed : Math.floor(Math.random() * (2**32 - 1)),
643
+ (userSeed <= 0),
644
+ parseFloat(cfgScaleInput.value),
645
+ true
646
+ ],
647
+ session_hash: currentSessionHash
648
+ };
649
+
650
+ const joinResponse = await submitToQueue(generationPayload);
651
+ if (joinResponse && joinResponse.event_id) {
652
+ listenForResults(joinResponse.event_id);
653
+ } else {
654
+ addStatusMessage('ارسال درخواست به سرور ناموفق بود.', 'error');
655
+ generateButton.disabled = false;
656
+ loader.style.display = 'none';
657
+ }
658
+ });
659
+ </script>
660
+ </p>