Ezmary commited on
Commit
0d87ed4
·
verified ·
1 Parent(s): acdd6e6

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +300 -355
index.html CHANGED
@@ -3,67 +3,56 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>ساخت ویدیو با Aging Face API</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 
 
 
14
  }
15
 
 
 
16
  body {
17
- background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
18
- color: #fff;
19
- min-height: 100vh;
 
20
  padding: 20px;
 
 
 
 
21
  }
22
 
23
  .container {
24
- max-width: 1200px;
25
- margin: 0 auto;
 
 
 
 
26
  }
27
 
28
- header {
29
  text-align: center;
30
- padding: 30px 0;
31
- margin-bottom: 30px;
32
- }
33
-
34
- header h1 {
35
- font-size: 2.8rem;
36
  margin-bottom: 10px;
37
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
38
- }
39
-
40
- header p {
41
- font-size: 1.2rem;
42
- opacity: 0.9;
43
- max-width: 700px;
44
- margin: 0 auto;
45
- }
46
-
47
- .card {
48
- background: rgba(255, 255, 255, 0.1);
49
- backdrop-filter: blur(10px);
50
- border-radius: 15px;
51
- padding: 25px;
52
- margin-bottom: 30px;
53
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
54
- border: 1px solid rgba(255, 255, 255, 0.1);
55
  }
56
 
57
- .card h2 {
58
- font-size: 1.8rem;
59
- margin-bottom: 20px;
60
- display: flex;
61
- align-items: center;
62
- gap: 10px;
63
  }
64
 
65
- .card h2 i {
66
- color: #fdbb2d;
67
  }
68
 
69
  .form-group {
@@ -73,124 +62,123 @@
73
  label {
74
  display: block;
75
  margin-bottom: 8px;
76
- font-weight: 600;
 
77
  }
78
 
79
- input, textarea, select {
 
 
80
  width: 100%;
81
- padding: 12px 15px;
82
- border: none;
 
83
  border-radius: 8px;
84
- background: rgba(255, 255, 255, 0.15);
85
- color: white;
86
  font-size: 1rem;
87
- transition: all 0.3s ease;
 
88
  }
89
 
90
- input:focus, textarea:focus, select:focus {
 
 
91
  outline: none;
92
- background: rgba(255, 255, 255, 0.25);
93
- box-shadow: 0 0 0 2px #fdbb2d;
94
  }
95
-
96
  input::placeholder, textarea::placeholder {
97
- color: rgba(255, 255, 255, 0.7);
98
  }
99
 
100
- button {
101
- background: linear-gradient(to right, #fdbb2d, #b21f1f);
102
- color: white;
103
- border: none;
104
- padding: 14px 25px;
105
- border-radius: 8px;
106
- font-size: 1.1rem;
107
- font-weight: 600;
108
- cursor: pointer;
109
- transition: all 0.3s ease;
110
  display: flex;
111
- align-items: center;
112
- justify-content: center;
113
- gap: 10px;
114
- width: 100%;
115
  }
116
 
117
- button:hover {
118
- transform: translateY(-3px);
119
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
 
 
 
 
 
 
 
 
120
  }
121
 
122
- button:disabled {
123
- background: #6c757d;
124
- cursor: not-allowed;
125
- transform: none;
126
- box-shadow: none;
127
  }
128
 
129
- .file-input-container {
130
- position: relative;
131
- overflow: hidden;
132
- display: inline-block;
133
- width: 100%;
134
  }
135
 
136
- .file-input-container input[type="file"] {
137
- position: absolute;
138
- left: 0;
139
- top: 0;
140
- opacity: 0;
141
- width: 100%;
142
- height: 100%;
143
- cursor: pointer;
144
  }
145
 
146
- .file-input-label {
147
- display: flex;
148
- align-items: center;
149
- justify-content: center;
150
- gap: 10px;
151
- padding: 12px 15px;
152
- background: rgba(255, 255, 255, 0.15);
153
  border-radius: 8px;
 
 
 
154
  cursor: pointer;
155
- transition: all 0.3s ease;
 
156
  }
157
 
158
- .file-input-label:hover {
159
- background: rgba(255, 255, 255, 0.25);
160
  }
161
-
162
- .preview-container {
163
- margin-top: 20px;
164
- text-align: center;
165
  }
166
 
167
- .preview-container img, .preview-container video {
168
- max-width: 100%;
169
- max-height: 300px;
170
- border-radius: 10px;
171
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
172
  }
173
 
174
  .result-container {
175
- display: none;
176
- margin-top: 30px;
177
- text-align: center;
 
 
 
 
 
178
  }
179
 
180
- .loading {
181
- display: none;
182
- text-align: center;
183
- padding: 20px;
 
 
 
184
  }
185
 
186
- .loading-spinner {
187
- border: 5px solid rgba(255, 255, 255, 0.3);
 
188
  border-radius: 50%;
189
- border-top: 5px solid #fdbb2d;
190
- width: 50px;
191
- height: 50px;
192
  animation: spin 1s linear infinite;
193
- margin: 0 auto 15px;
194
  }
195
 
196
  @keyframes spin {
@@ -198,284 +186,241 @@
198
  100% { transform: rotate(360deg); }
199
  }
200
 
201
- .tabs {
202
- display: flex;
203
- margin-bottom: 20px;
204
- border-radius: 10px;
205
- overflow: hidden;
206
- background: rgba(0, 0, 0, 0.2);
207
- }
208
-
209
- .tab {
210
- flex: 1;
211
  text-align: center;
212
- padding: 15px;
213
- cursor: pointer;
214
- transition: all 0.3s ease;
215
- font-weight: 600;
216
  }
217
 
218
- .tab.active {
219
- background: #fdbb2d;
220
- color: #1a2a6c;
221
  }
222
-
223
- .tab-content {
224
  display: none;
225
  }
226
 
227
- .tab-content.active {
228
- display: block;
229
- }
230
-
231
- .alert {
232
- padding: 15px;
233
  border-radius: 8px;
234
- margin-bottom: 20px;
235
- display: none;
236
- }
237
-
238
- .alert-success {
239
- background: rgba(40, 167, 69, 0.3);
240
- border: 1px solid #28a745;
241
- }
242
-
243
- .alert-error {
244
- background: rgba(220, 53, 69, 0.3);
245
- border: 1px solid #dc3545;
246
- }
247
-
248
- footer {
249
  text-align: center;
250
- margin-top: 40px;
251
- padding: 20px;
252
- font-size: 0.9rem;
253
- opacity: 0.7;
254
  }
255
-
256
- @media (max-width: 768px) {
257
- header h1 {
258
- font-size: 2.2rem;
259
- }
260
-
261
- .card {
262
- padding: 20px;
263
- }
264
  }
 
265
  </style>
266
  </head>
267
  <body>
 
268
  <div class="container">
269
- <header>
270
- <h1>ساخت ویدیو با Aging Face API</h1>
271
- <p>با استفاده از این ابزار می‌توانید از متن یا تصویر، ویدیوهای جذاب ایجاد کنید</p>
272
- </header>
273
 
274
- <div class="card">
275
- <div class="form-group">
276
- <label for="api-key"><i class="fas fa-key"></i> کلید API Aging Face</label>
277
- <input type="text" id="api-key" placeholder="کلید API خود را اینجا وارد کنید">
278
- </div>
279
  </div>
280
 
281
- <div class="card">
282
- <div class="tabs">
283
- <div class="tab active" data-tab="text-to-video">ساخت ویدیو از متن</div>
284
- <div class="tab" data-tab="image-to-video">ساخت ویدیو از تصویر</div>
285
- </div>
286
 
287
- <div class="tab-content active" id="text-to-video">
288
- <h2><i class="fas fa-font"></i> ساخت ویدیو از متن</h2>
289
-
290
- <div class="form-group">
291
- <label for="text-input">متن خود را وارد کنید</label>
292
- <textarea id="text-input" rows="4" placeholder="مثلاً: یک مرد جوان در حال قدم زدن در خیابان"></textarea>
293
- </div>
294
-
295
- <button id="generate-from-text">
296
- <i class="fas fa-video"></i> تولید ویدیو
297
- </button>
298
  </div>
299
-
300
- <div class="tab-content" id="image-to-video">
301
- <h2><i class="fas fa-image"></i> ساخت ویدیو از تصویر</h2>
302
-
303
- <div class="form-group">
304
- <label for="image-input">تصویر خود را انتخاب کنید</label>
305
- <div class="file-input-container">
306
- <div class="file-input-label">
307
- <i class="fas fa-upload"></i>
308
- <span id="file-name">انتخاب فایل تصویر</span>
309
- </div>
310
- <input type="file" id="image-input" accept="image/*">
311
- </div>
312
- </div>
313
-
314
- <div class="form-group">
315
- <label for="image-prompt">توضیحات برای ویدیو</label>
316
- <input type="text" id="image-prompt" placeholder="مثلاً: گربه شروع به رقصیدن می‌کند">
317
- </div>
318
-
319
- <button id="generate-from-image">
320
- <i class="fas fa-video"></i> تولید ویدیو
321
- </button>
322
  </div>
 
323
  </div>
324
 
325
- <div class="alert alert-success" id="success-alert">
326
- <i class="fas fa-check-circle"></i> ویدیو با موفقیت تولید شد!
327
- </div>
328
-
329
- <div class="alert alert-error" id="error-alert">
330
- <i class="fas fa-exclamation-circle"></i> خطا در تولید ویدیو. لطفاً دوباره تلاش کنید.
331
- </div>
332
-
333
- <div class="loading" id="loading">
334
- <div class="loading-spinner"></div>
335
- <p>در حال تولید ویدیو، لطفاً منتظر بمانید...</p>
336
- </div>
337
-
338
- <div class="card result-container" id="result-container">
339
- <h2><i class="fas fa-film"></i> ویدیو تولید شده</h2>
340
- <div class="preview-container">
341
- <video id="generated-video" controls>
342
- مرورگر شما از تگ ویدیو پشتیبانی نمی‌کند.
343
- </video>
344
  </div>
345
- <div class="form-group" style="margin-top: 20px;">
346
- <button id="download-video">
347
- <i class="fas fa-download"></i> دانلود ویدیو
348
- </button>
 
349
  </div>
 
 
 
 
 
 
 
 
 
350
  </div>
351
-
352
- <footer>
353
- <p>ساخته شده با ❤️ | از Aging Face API استفاده می‌کند</p>
354
- </footer>
355
  </div>
356
 
357
- <script>
358
- // مدیریت تب‌ها
359
- document.querySelectorAll('.tab').forEach(tab => {
360
- tab.addEventListener('click', () => {
361
- // حذف کلاس active از همه تب‌ها و محتواها
362
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
363
- document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
364
-
365
- // اضافه کردن کلاس active به تب و محتوای انتخاب شده
366
- tab.classList.add('active');
367
- document.getElementById(tab.dataset.tab).classList.add('active');
368
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  });
370
 
371
- // نمایش نام فایل انتخاب شده
372
- document.getElementById('image-input').addEventListener('change', function() {
373
- const fileName = this.files[0] ? this.files[0].name : 'انتخاب فایل تصویر';
374
- document.getElementById('file-name').textContent = fileName;
375
- });
 
 
 
 
 
 
 
 
 
 
 
376
 
377
- // مدیریت تولید ویدیو از متن
378
- document.getElementById('generate-from-text').addEventListener('click', async function() {
379
- const apiKey = document.getElementById('api-key').value.trim();
380
- const textInput = document.getElementById('text-input').value.trim();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
- if (!apiKey) {
383
- showAlert('error', 'لطفاً کلید API را وارد کنید.');
 
384
  return;
385
  }
386
-
387
- if (!textInput) {
388
- showAlert('error', 'لطفاً متن خود را وارد کنید.');
389
- return;
 
 
 
 
 
 
 
 
 
390
  }
391
-
392
- await generateVideo('text', { text: textInput }, apiKey);
393
  });
394
 
395
- // مدیریت تولید ویدیو از تصویر
396
- document.getElementById('generate-from-image').addEventListener('click', async function() {
397
- const apiKey = document.getElementById('api-key').value.trim();
398
- const imageInput = document.getElementById('image-input');
399
- const imagePrompt = document.getElementById('image-prompt').value.trim();
400
-
401
- if (!apiKey) {
402
- showAlert('error', 'لطفاً کلید API را وارد کنید.');
403
- return;
404
- }
405
-
406
- if (!imageInput.files.length) {
407
- showAlert('error', 'لطفاً یک تصویر انتخاب کنید.');
408
  return;
409
  }
410
 
411
- if (!imagePrompt) {
412
- showAlert('error', 'لطفاً توضیحات برای ویدیو را وارد کنید.');
 
413
  return;
414
  }
415
 
416
- await generateVideo('image', {
417
- image: imageInput.files[0],
418
- prompt: imagePrompt
419
- }, apiKey);
420
- });
421
-
422
- // مدیریت دانلود ویدیو
423
- document.getElementById('download-video').addEventListener('click', function() {
424
- const videoElement = document.getElementById('generated-video');
425
- const videoSrc = videoElement.src;
426
-
427
- if (videoSrc) {
428
- const a = document.createElement('a');
429
- a.href = videoSrc;
430
- a.download = 'generated-video.mp4';
431
- document.body.appendChild(a);
432
- a.click();
433
- document.body.removeChild(a);
434
- }
435
- });
436
-
437
- // تابع برای نمایش آلرت
438
- function showAlert(type, message) {
439
- const alertElement = type === 'success'
440
- ? document.getElementById('success-alert')
441
- : document.getElementById('error-alert');
442
-
443
- alertElement.innerHTML = `<i class="fas fa-${type === 'success' ? 'check' : 'exclamation'}-circle"></i> ${message}`;
444
- alertElement.style.display = 'block';
445
-
446
- setTimeout(() => {
447
- alertElement.style.display = 'none';
448
- }, 5000);
449
- }
450
 
451
- // تابع اصلی برای تولید ویدیو
452
- async function generateVideo(type, data, apiKey) {
453
- // نمایش حالت بارگذاری
454
- document.getElementById('loading').style.display = 'block';
455
- document.getElementById('result-container').style.display = 'none';
456
-
457
  try {
458
- // در اینجا باید کد واقعی برای ارتباط با API نوشته شود
459
- // این یک شبیه‌سازی است
460
-
461
- // شبیه‌سازی تاخیر در تولید ویدیو
462
- await new Promise(resolve => setTimeout(resolve, 3000));
463
-
464
- // نمایش نتیجه
465
- document.getElementById('loading').style.display = 'none';
466
- document.getElementById('result-container').style.display = 'block';
467
-
468
- // در اینجا باید ویدیوی تولید شده از API دریافت و نمایش داده شود
469
- // برای نمونه، از یک ویدیوی نمونه استفاده می‌کنیم
470
- document.getElementById('generated-video').src = 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4';
471
-
472
- showAlert('success', 'ویدیو با موفقیت تولید شد!');
473
  } catch (error) {
474
- console.error('Error generating video:', error);
475
- document.getElementById('loading').style.display = 'none';
476
- showAlert('error', 'خطا در تولید ویدیو. لطفاً دوباره تلاش کنید.');
477
  }
478
- }
 
479
  </script>
480
  </body>
481
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>مولد ویدیو با Hugging Face</title>
 
7
  <style>
8
+ :root {
9
+ --bg-color: #121212;
10
+ --surface-color: #1e1e1e;
11
+ --primary-color: #03dac6;
12
+ --primary-variant-color: #3700b3;
13
+ --on-bg-color: #e0e0e0;
14
+ --on-surface-color: #ffffff;
15
+ --error-color: #cf6679;
16
  }
17
 
18
+ @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap');
19
+
20
  body {
21
+ font-family: 'Vazirmatn', sans-serif;
22
+ background-color: var(--bg-color);
23
+ color: var(--on-bg-color);
24
+ margin: 0;
25
  padding: 20px;
26
+ display: flex;
27
+ justify-content: center;
28
+ align-items: flex-start;
29
+ min-height: 100vh;
30
  }
31
 
32
  .container {
33
+ width: 100%;
34
+ max-width: 800px;
35
+ background-color: var(--surface-color);
36
+ border-radius: 12px;
37
+ padding: 25px;
38
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
39
  }
40
 
41
+ h1 {
42
  text-align: center;
43
+ color: var(--on-surface-color);
 
 
 
 
 
44
  margin-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
 
47
+ p.subtitle {
48
+ text-align: center;
49
+ margin-top: 0;
50
+ margin-bottom: 25px;
51
+ color: #aaa;
 
52
  }
53
 
54
+ .token-section {
55
+ margin-bottom: 25px;
56
  }
57
 
58
  .form-group {
 
62
  label {
63
  display: block;
64
  margin-bottom: 8px;
65
+ font-weight: bold;
66
+ font-size: 1rem;
67
  }
68
 
69
+ input[type="text"],
70
+ input[type="password"],
71
+ textarea {
72
  width: 100%;
73
+ padding: 12px;
74
+ background-color: #2c2c2c;
75
+ border: 1px solid #444;
76
  border-radius: 8px;
77
+ color: var(--on-bg-color);
 
78
  font-size: 1rem;
79
+ box-sizing: border-box;
80
+ transition: border-color 0.3s, box-shadow 0.3s;
81
  }
82
 
83
+ input[type="text"]:focus,
84
+ input[type="password"]:focus,
85
+ textarea:focus {
86
  outline: none;
87
+ border-color: var(--primary-color);
88
+ box-shadow: 0 0 0 3px rgba(3, 218, 198, 0.2);
89
  }
90
+
91
  input::placeholder, textarea::placeholder {
92
+ color: #777;
93
  }
94
 
95
+ .tabs {
 
 
 
 
 
 
 
 
 
96
  display: flex;
97
+ border-bottom: 1px solid #444;
98
+ margin-bottom: 20px;
 
 
99
  }
100
 
101
+ .tab-button {
102
+ padding: 10px 20px;
103
+ cursor: pointer;
104
+ border: none;
105
+ background-color: transparent;
106
+ color: #aaa;
107
+ font-size: 1rem;
108
+ font-family: 'Vazirmatn', sans-serif;
109
+ font-weight: bold;
110
+ border-bottom: 3px solid transparent;
111
+ transition: color 0.3s, border-color 0.3s;
112
  }
113
 
114
+ .tab-button.active {
115
+ color: var(--primary-color);
116
+ border-bottom-color: var(--primary-color);
 
 
117
  }
118
 
119
+ .tab-content {
120
+ display: none;
 
 
 
121
  }
122
 
123
+ .tab-content.active {
124
+ display: block;
 
 
 
 
 
 
125
  }
126
 
127
+ button {
128
+ background-color: var(--primary-color);
129
+ color: #000;
130
+ border: none;
131
+ padding: 12px 20px;
 
 
132
  border-radius: 8px;
133
+ font-size: 1rem;
134
+ font-weight: bold;
135
+ font-family: 'Vazirmatn', sans-serif;
136
  cursor: pointer;
137
+ transition: background-color 0.3s, transform 0.1s;
138
+ width: 100%;
139
  }
140
 
141
+ button:hover:not(:disabled) {
142
+ background-color: #03c0b1;
143
  }
144
+
145
+ button:active:not(:disabled) {
146
+ transform: scale(0.98);
 
147
  }
148
 
149
+ button:disabled {
150
+ background-color: #555;
151
+ color: #999;
152
+ cursor: not-allowed;
 
153
  }
154
 
155
  .result-container {
156
+ margin-top: 25px;
157
+ padding: 15px;
158
+ background-color: #2c2c2c;
159
+ border-radius: 8px;
160
+ min-height: 50px;
161
+ display: flex;
162
+ justify-content: center;
163
+ align-items: center;
164
  }
165
 
166
+ video, img#imagePreview {
167
+ max-width: 100%;
168
+ border-radius: 8px;
169
+ }
170
+
171
+ #imagePreview {
172
+ max-height: 250px;
173
  }
174
 
175
+ .loader {
176
+ border: 4px solid #f3f3f3;
177
+ border-top: 4px solid var(--primary-color);
178
  border-radius: 50%;
179
+ width: 30px;
180
+ height: 30px;
 
181
  animation: spin 1s linear infinite;
 
182
  }
183
 
184
  @keyframes spin {
 
186
  100% { transform: rotate(360deg); }
187
  }
188
 
189
+ .status-message {
190
+ margin-top: 15px;
 
 
 
 
 
 
 
 
191
  text-align: center;
192
+ color: #ccc;
 
 
 
193
  }
194
 
195
+ .error-message {
196
+ color: var(--error-color);
197
+ font-weight: bold;
198
  }
199
+
200
+ input[type="file"] {
201
  display: none;
202
  }
203
 
204
+ .file-label {
205
+ display: inline-block;
206
+ padding: 10px 15px;
207
+ background-color: #333;
208
+ border: 1px dashed #666;
 
209
  border-radius: 8px;
210
+ cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  text-align: center;
212
+ width: 100%;
213
+ box-sizing: border-box;
214
+ transition: background-color 0.3s;
 
215
  }
216
+ .file-label:hover {
217
+ background-color: #444;
 
 
 
 
 
 
 
218
  }
219
+
220
  </style>
221
  </head>
222
  <body>
223
+
224
  <div class="container">
225
+ <h1>مولد ویدیو با Hugging Face</h1>
226
+ <p class="subtitle">ویدیوهای خلاقانه از متن یا تصویر بسازید</p>
 
 
227
 
228
+ <div class="token-section form-group">
229
+ <label for="hfToken">کلید توکن Hugging Face</label>
230
+ <input type="password" id="hfToken" placeholder="توکن خود را اینجا وارد کنید (مثال: hf_...)">
 
 
231
  </div>
232
 
233
+ <div class="tabs">
234
+ <button class="tab-button active" onclick="openTab('textToVideo')">ساخت ویدیو از متن</button>
235
+ <button class="tab-button" onclick="openTab('imageToVideo')">ساخت ویدیو از تصویر</button>
236
+ </div>
 
237
 
238
+ <!-- Tab Content: Text to Video -->
239
+ <div id="textToVideo" class="tab-content active">
240
+ <div class="form-group">
241
+ <label for="textPrompt">توصیف ویدیو (Prompt)</label>
242
+ <textarea id="textPrompt" rows="4" placeholder="مثال: A young man walking on the street"></textarea>
 
 
 
 
 
 
243
  </div>
244
+ <button id="generateTextBtn">ساخت ویدیو</button>
245
+ <div class="result-container" id="textResultContainer">
246
+ <span>نتیجه در اینجا نمایش داده می‌شود</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  </div>
248
+ <div class="status-message" id="textStatus"></div>
249
  </div>
250
 
251
+ <!-- Tab Content: Image to Video -->
252
+ <div id="imageToVideo" class="tab-content">
253
+ <div class="form-group">
254
+ <label for="imageFile">انتخاب تصویر</label>
255
+ <input type="file" id="imageFile" accept="image/*">
256
+ <label for="imageFile" class="file-label">برای انتخاب تصویر کلیک کنید</label>
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  </div>
258
+ <div class="form-group" id="imagePreviewContainer" style="display: none;">
259
+ <label>پیش‌نمایش تصویر</label>
260
+ <div class="result-container">
261
+ <img id="imagePreview" src="" alt="Image Preview">
262
+ </div>
263
  </div>
264
+ <div class="form-group">
265
+ <label for="imagePrompt">توصیف حرکت (Prompt)</label>
266
+ <input type="text" id="imagePrompt" placeholder="مثال: The cat starts to dance">
267
+ </div>
268
+ <button id="generateImageBtn">ساخت ویدیو</button>
269
+ <div class="result-container" id="imageResultContainer">
270
+ <span>نتیجه در اینجا نمایش داده می‌شود</span>
271
+ </div>
272
+ <div class="status-message" id="imageStatus"></div>
273
  </div>
 
 
 
 
274
  </div>
275
 
276
+ <script type="module">
277
+ // Import the HfInference class from the CDN
278
+ import { HfInference } from "https://cdn.jsdelivr.net/npm/@huggingface/inference@2.7.0/+esm";
279
+
280
+ // DOM Elements
281
+ const hfTokenInput = document.getElementById('hfToken');
282
+
283
+ // Text-to-Video elements
284
+ const textPrompt = document.getElementById('textPrompt');
285
+ const generateTextBtn = document.getElementById('generateTextBtn');
286
+ const textResultContainer = document.getElementById('textResultContainer');
287
+ const textStatus = document.getElementById('textStatus');
288
+
289
+ // Image-to-Video elements
290
+ const imageFile = document.getElementById('imageFile');
291
+ const imagePrompt = document.getElementById('imagePrompt');
292
+ const generateImageBtn = document.getElementById('generateImageBtn');
293
+ const imageResultContainer = document.getElementById('imageResultContainer');
294
+ const imageStatus = document.getElementById('imageStatus');
295
+ const imagePreviewContainer = document.getElementById('imagePreviewContainer');
296
+ const imagePreview = document.getElementById('imagePreview');
297
+
298
+ // --- Tab Management ---
299
+ window.openTab = function(tabName) {
300
+ const tabContents = document.querySelectorAll('.tab-content');
301
+ tabContents.forEach(content => content.classList.remove('active'));
302
+
303
+ const tabButtons = document.querySelectorAll('.tab-button');
304
+ tabButtons.forEach(button => button.classList.remove('active'));
305
+
306
+ document.getElementById(tabName).classList.add('active');
307
+ event.currentTarget.classList.add('active');
308
+ }
309
+
310
+ // --- Image Preview ---
311
+ imageFile.addEventListener('change', () => {
312
+ const file = imageFile.files[0];
313
+ if (file) {
314
+ const reader = new FileReader();
315
+ reader.onload = (e) => {
316
+ imagePreview.src = e.target.result;
317
+ imagePreviewContainer.style.display = 'block';
318
+ };
319
+ reader.readAsDataURL(file);
320
+ } else {
321
+ imagePreviewContainer.style.display = 'none';
322
+ }
323
  });
324
 
325
+ // --- Helper Functions ---
326
+ function getHfClient() {
327
+ const token = hfTokenInput.value.trim();
328
+ if (!token) {
329
+ alert('لطفاً کلید توکن Hugging Face خود را وارد کنید.');
330
+ return null;
331
+ }
332
+ return new HfInference(token);
333
+ }
334
+
335
+ function showLoading(container, statusEl, buttonEl) {
336
+ container.innerHTML = '<div class="loader"></div>';
337
+ statusEl.className = 'status-message';
338
+ statusEl.textContent = 'در حال ساخت ویدیو... این فرآیند ممکن است چند دقیقه طول بکشد. لطفاً صبور باشید.';
339
+ buttonEl.disabled = true;
340
+ }
341
 
342
+ function showResult(container, statusEl, buttonEl, videoBlob) {
343
+ const videoUrl = URL.createObjectURL(videoBlob);
344
+ container.innerHTML = `<video controls src="${videoUrl}"></video>`;
345
+ statusEl.textContent = 'ویدیو با موفقیت ساخته شد!';
346
+ buttonEl.disabled = false;
347
+ }
348
+
349
+ function showError(container, statusEl, buttonEl, error) {
350
+ container.innerHTML = '<span>خطا در ساخت ویدیو</span>';
351
+ statusEl.className = 'status-message error-message';
352
+ statusEl.textContent = `خطا: ${error.message}`;
353
+ buttonEl.disabled = false;
354
+ }
355
+
356
+ // --- Event Listeners for Generation ---
357
+
358
+ // Text-to-Video Generation
359
+ generateTextBtn.addEventListener('click', async () => {
360
+ const hf = getHfClient();
361
+ if (!hf) return;
362
 
363
+ const prompt = textPrompt.value.trim();
364
+ if (!prompt) {
365
+ alert('لطفاً توصیف ویدیو را وارد کنید.');
366
  return;
367
  }
368
+
369
+ showLoading(textResultContainer, textStatus, generateTextBtn);
370
+
371
+ try {
372
+ const videoBlob = await hf.textToVideo({
373
+ provider: "fal-ai",
374
+ model: "akhaliq/veo3.1-fast",
375
+ inputs: prompt,
376
+ });
377
+ showResult(textResultContainer, textStatus, generateTextBtn, videoBlob);
378
+ } catch (error) {
379
+ console.error("Text-to-Video Error:", error);
380
+ showError(textResultContainer, textStatus, generateTextBtn, error);
381
  }
 
 
382
  });
383
 
384
+ // Image-to-Video Generation (CORRECTED)
385
+ generateImageBtn.addEventListener('click', async () => {
386
+ const hf = getHfClient();
387
+ if (!hf) return;
388
+
389
+ const file = imageFile.files[0];
390
+ if (!file) {
391
+ alert('لطفاً یک تصویر انتخاب کنید.');
 
 
 
 
 
392
  return;
393
  }
394
 
395
+ const prompt = imagePrompt.value.trim();
396
+ if (!prompt) {
397
+ alert('لطفاً توصیف حرکت را وارد کنید.');
398
  return;
399
  }
400
 
401
+ showLoading(imageResultContainer, imageStatus, generateImageBtn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
 
 
 
 
 
 
 
403
  try {
404
+ // CORRECTED: Use the generic 'request' method
405
+ const videoBlob = await hf.request({
406
+ task: "image-to-video",
407
+ model: "akhaliq/veo3.1-fast-image-to-video",
408
+ data: file, // The library handles the File object
409
+ parameters: {
410
+ prompt: prompt,
411
+ provider: "fal-ai"
412
+ }
413
+ });
414
+
415
+ // The result of hf.request is directly the blob
416
+ showResult(imageResultContainer, imageStatus, generateImageBtn, videoBlob);
417
+
 
418
  } catch (error) {
419
+ console.error("Image-to-Video Error:", error);
420
+ showError(imageResultContainer, imageStatus, generateImageBtn, error);
 
421
  }
422
+ });
423
+
424
  </script>
425
  </body>
426
  </html>