Athspi commited on
Commit
7ddde4b
·
verified ·
1 Parent(s): 3391bce

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +203 -136
templates/index.html CHANGED
@@ -175,6 +175,13 @@
175
  box-shadow: 0 5px 15px rgba(63, 55, 201, 0.3);
176
  }
177
 
 
 
 
 
 
 
 
178
  .btn i {
179
  font-size: 1.2rem;
180
  }
@@ -201,71 +208,63 @@
201
  border: 1px solid rgba(247, 37, 133, 0.2);
202
  }
203
 
204
- .result-section {
205
- margin-top: 2rem;
206
- padding: 1.5rem;
207
- background: #f8f9fe;
208
- border-radius: var(--border-radius);
209
  display: none;
210
  }
211
 
212
- .result-title {
213
- font-size: 1.3rem;
214
- margin-bottom: 1rem;
215
- color: var(--secondary);
216
  display: flex;
217
- align-items: center;
218
- gap: 0.5rem;
219
  }
220
 
221
- .result-title i {
222
- font-size: 1.2rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
 
225
  .result-video {
226
  width: 100%;
227
  border-radius: var(--border-radius);
228
  margin-bottom: 1.5rem;
229
- aspect-ratio: 16 / 9;
230
  background: black;
231
  }
232
 
233
- .script-box {
234
  background: var(--dark);
235
  color: white;
236
  padding: 1.5rem;
237
  border-radius: var(--border-radius);
238
  max-height: 300px;
239
  overflow-y: auto;
240
- font-family: monospace;
241
- white-space: pre-wrap;
242
- line-height: 1.6;
243
- }
244
-
245
- .loader {
246
- display: none;
247
- text-align: center;
248
- padding: 2rem;
249
- }
250
-
251
- .spinner {
252
- width: 50px;
253
- height: 50px;
254
- border: 5px solid rgba(67, 97, 238, 0.2);
255
- border-top: 5px solid var(--primary);
256
- border-radius: 50%;
257
- animation: spin 1s linear infinite;
258
- margin: 0 auto 1rem;
259
- }
260
-
261
- .loading-text {
262
- font-weight: 500;
263
- color: var(--dark);
264
- }
265
-
266
- @keyframes spin {
267
- 0% { transform: rotate(0deg); }
268
- 100% { transform: rotate(360deg); }
269
  }
270
 
271
  @media (max-width: 768px) {
@@ -300,12 +299,7 @@
300
  </header>
301
 
302
  <div class="content">
303
- <div id="alert" class="alert" style="display: none;">
304
- <i class="fas fa-info-circle"></i>
305
- <span id="alert-message"></span>
306
- </div>
307
-
308
- <form id="dubbing-form">
309
  <div class="upload-area" id="upload-area">
310
  <div class="upload-icon">
311
  <i class="fas fa-cloud-upload-alt"></i>
@@ -313,7 +307,7 @@
313
  <h3>Drag & Drop Video File</h3>
314
  <p>or click to browse (MP4, MOV, WEBM, AVI)</p>
315
  <div id="file-info" class="file-info">No file selected</div>
316
- <input type="file" id="video" name="video" class="file-input" accept="video/*">
317
  </div>
318
 
319
  <div class="options">
@@ -322,15 +316,13 @@
322
  <i class="fas fa-microphone"></i>
323
  Voice Style
324
  </div>
325
- <div class="radio-group">
326
- <label class="radio-option">
327
- <input type="radio" name="voice" value="male" checked>
328
- Male Voice
329
- </label>
330
- <label class="radio-option">
331
- <input type="radio" name="voice" value="female">
332
- Female Voice
333
- </label>
334
  </div>
335
  </div>
336
 
@@ -341,77 +333,97 @@
341
  </div>
342
  <div class="checkbox-group">
343
  <label class="checkbox-option">
344
- <input type="checkbox" name="cheerful">
345
  Cheerful Tone
346
  </label>
347
  </div>
348
  </div>
349
  </div>
350
 
351
- <button type="submit" class="btn">
352
  <i class="fas fa-magic"></i>
353
  Generate Dubbed Video
354
  </button>
355
- </form>
356
-
357
- <div class="loader" id="loader">
358
- <div class="spinner"></div>
359
- <p class="loading-text">Processing your video. Please wait...</p>
360
  </div>
361
 
362
- <div class="result-section" id="result-section">
363
- <div class="result-title">
364
- <i class="fas fa-video"></i>
365
- Your Dubbed Video
 
 
 
366
  </div>
 
 
 
 
 
 
 
367
  <video controls class="result-video" id="result-video">
368
  Your browser does not support the video tag.
369
  </video>
370
 
371
- <div class="result-title">
372
- <i class="fas fa-scroll"></i>
373
- Generated Script
374
- </div>
375
- <div class="script-box" id="script-box"></div>
376
  </div>
377
  </div>
378
  </div>
379
 
380
  <script>
381
- const form = document.getElementById('dubbing-form');
382
- const loader = document.getElementById('loader');
383
  const uploadArea = document.getElementById('upload-area');
384
- const fileInput = document.getElementById('video');
385
  const fileInfo = document.getElementById('file-info');
386
- const resultSection = document.getElementById('result-section');
 
 
 
 
 
 
387
  const resultVideo = document.getElementById('result-video');
388
- const scriptBox = document.getElementById('script-box');
389
- const alertBox = document.getElementById('alert');
390
- const alertMessage = document.getElementById('alert-message');
 
 
 
 
 
 
 
 
 
391
 
392
- // Handle file selection
393
- fileInput.addEventListener('change', function() {
394
  if (this.files.length > 0) {
395
  fileInfo.textContent = this.files[0].name;
396
  uploadArea.style.borderColor = '#4361ee';
397
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
398
- resultSection.style.display = 'none';
 
 
 
399
  }
400
- });
401
 
402
- // Drag and drop functionality
403
- uploadArea.addEventListener('dragover', (e) => {
404
  e.preventDefault();
405
  uploadArea.style.borderColor = '#4361ee';
406
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.1)';
407
- });
408
 
409
- uploadArea.addEventListener('dragleave', () => {
410
  uploadArea.style.borderColor = fileInput.files.length > 0 ? '#4361ee' : '#4895ef';
411
- uploadArea.style.backgroundColor = fileInput.files.length > 0 ? 'rgba(67, 97, 238, 0.05)' : 'transparent';
412
- });
 
 
413
 
414
- uploadArea.addEventListener('drop', (e) => {
415
  e.preventDefault();
416
  uploadArea.style.borderColor = '#4361ee';
417
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
@@ -419,61 +431,116 @@
419
  if (e.dataTransfer.files.length) {
420
  fileInput.files = e.dataTransfer.files;
421
  fileInfo.textContent = e.dataTransfer.files[0].name;
422
- resultSection.style.display = 'none';
 
 
 
423
  }
424
- });
425
 
426
- // Form submission
427
- form.addEventListener('submit', async (e) => {
428
- e.preventDefault();
429
-
430
  if (!fileInput.files.length) {
431
- showAlert('Please select a video file first.', 'error');
432
  return;
433
  }
434
 
435
- try {
436
- form.style.display = 'none';
437
- loader.style.display = 'block';
438
- alertBox.style.display = 'none';
439
-
440
- const formData = new FormData();
441
- formData.append('file', fileInput.files[0]);
442
- formData.append('voice', document.querySelector('input[name="voice"]:checked').value);
443
- formData.append('cheerful', document.querySelector('input[name="cheerful"]').checked);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
 
445
- const response = await fetch('/process', {
446
- method: 'POST',
447
- body: formData
448
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
449
 
450
- if (!response.ok) {
451
- const error = await response.json();
452
- throw new Error(error.detail || 'Processing failed');
453
- }
 
 
 
 
454
 
455
- const data = await response.json();
 
 
 
456
 
457
- resultVideo.src = data.video_url;
458
- scriptBox.textContent = data.script;
459
- resultSection.style.display = 'block';
 
 
460
 
461
- } catch (error) {
462
- showAlert(error.message, 'error');
463
- console.error('Processing error:', error);
464
- } finally {
465
- form.style.display = 'block';
466
- loader.style.display = 'none';
 
 
 
 
 
 
467
  }
468
- });
469
 
470
- function showAlert(message, type) {
471
- alertMessage.textContent = message;
472
- alertBox.className = `alert alert-${type}`;
473
- alertBox.style.display = 'flex';
474
-
475
- // Scroll to alert
476
- alertBox.scrollIntoView({ behavior: 'smooth' });
477
  }
478
  </script>
479
  </body>
 
175
  box-shadow: 0 5px 15px rgba(63, 55, 201, 0.3);
176
  }
177
 
178
+ .btn:disabled {
179
+ background: var(--gray);
180
+ cursor: not-allowed;
181
+ transform: none;
182
+ box-shadow: none;
183
+ }
184
+
185
  .btn i {
186
  font-size: 1.2rem;
187
  }
 
208
  border: 1px solid rgba(247, 37, 133, 0.2);
209
  }
210
 
211
+ .progress-container {
212
+ margin: 2rem 0;
 
 
 
213
  display: none;
214
  }
215
 
216
+ .progress-header {
 
 
 
217
  display: flex;
218
+ justify-content: space-between;
219
+ margin-bottom: 0.5rem;
220
  }
221
 
222
+ .progress-bar {
223
+ height: 10px;
224
+ background: #e9ecef;
225
+ border-radius: 5px;
226
+ overflow: hidden;
227
+ }
228
+
229
+ .progress-fill {
230
+ height: 100%;
231
+ background: var(--primary);
232
+ width: 0%;
233
+ transition: width 0.3s ease;
234
+ }
235
+
236
+ .progress-details {
237
+ margin-top: 1rem;
238
+ font-size: 0.9rem;
239
+ color: var(--gray);
240
+ }
241
+
242
+ .result-container {
243
+ display: none;
244
+ margin-top: 2rem;
245
+ animation: fadeIn 0.5s ease;
246
+ }
247
+
248
+ @keyframes fadeIn {
249
+ from { opacity: 0; }
250
+ to { opacity: 1; }
251
  }
252
 
253
  .result-video {
254
  width: 100%;
255
  border-radius: var(--border-radius);
256
  margin-bottom: 1.5rem;
257
+ aspect-ratio: 16/9;
258
  background: black;
259
  }
260
 
261
+ .script-container {
262
  background: var(--dark);
263
  color: white;
264
  padding: 1.5rem;
265
  border-radius: var(--border-radius);
266
  max-height: 300px;
267
  overflow-y: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
 
270
  @media (max-width: 768px) {
 
299
  </header>
300
 
301
  <div class="content">
302
+ <div id="upload-container">
 
 
 
 
 
303
  <div class="upload-area" id="upload-area">
304
  <div class="upload-icon">
305
  <i class="fas fa-cloud-upload-alt"></i>
 
307
  <h3>Drag & Drop Video File</h3>
308
  <p>or click to browse (MP4, MOV, WEBM, AVI)</p>
309
  <div id="file-info" class="file-info">No file selected</div>
310
+ <input type="file" id="video-input" class="file-input" accept="video/*">
311
  </div>
312
 
313
  <div class="options">
 
316
  <i class="fas fa-microphone"></i>
317
  Voice Style
318
  </div>
319
+ <div class="radio-group" id="voice-options">
320
+ {% for voice, value in voices.items() %}
321
+ <label class="radio-option">
322
+ <input type="radio" name="voice" value="{{ value }}" {% if loop.first %}checked{% endif %}>
323
+ {{ voice }}
324
+ </label>
325
+ {% endfor %}
 
 
326
  </div>
327
  </div>
328
 
 
333
  </div>
334
  <div class="checkbox-group">
335
  <label class="checkbox-option">
336
+ <input type="checkbox" name="tone" id="tone-option">
337
  Cheerful Tone
338
  </label>
339
  </div>
340
  </div>
341
  </div>
342
 
343
+ <button id="process-btn" class="btn" disabled>
344
  <i class="fas fa-magic"></i>
345
  Generate Dubbed Video
346
  </button>
 
 
 
 
 
347
  </div>
348
 
349
+ <div class="progress-container" id="progress-container">
350
+ <div class="progress-header">
351
+ <span>Processing your video</span>
352
+ <span id="eta">Estimating time...</span>
353
+ </div>
354
+ <div class="progress-bar">
355
+ <div class="progress-fill" id="progress-fill"></div>
356
  </div>
357
+ <div class="progress-details" id="progress-message">
358
+ Preparing to process your video...
359
+ </div>
360
+ </div>
361
+
362
+ <div class="result-container" id="result-container">
363
+ <h2>Your Dubbed Video</h2>
364
  <video controls class="result-video" id="result-video">
365
  Your browser does not support the video tag.
366
  </video>
367
 
368
+ <h2>Generated Script</h2>
369
+ <div class="script-container" id="script-container"></div>
 
 
 
370
  </div>
371
  </div>
372
  </div>
373
 
374
  <script>
375
+ // DOM Elements
 
376
  const uploadArea = document.getElementById('upload-area');
377
+ const fileInput = document.getElementById('video-input');
378
  const fileInfo = document.getElementById('file-info');
379
+ const processBtn = document.getElementById('process-btn');
380
+ const uploadContainer = document.getElementById('upload-container');
381
+ const progressContainer = document.getElementById('progress-container');
382
+ const progressFill = document.getElementById('progress-fill');
383
+ const progressMessage = document.getElementById('progress-message');
384
+ const etaDisplay = document.getElementById('eta');
385
+ const resultContainer = document.getElementById('result-container');
386
  const resultVideo = document.getElementById('result-video');
387
+ const scriptContainer = document.getElementById('script-container');
388
+
389
+ // State
390
+ let currentTaskId = null;
391
+ let statusCheckInterval = null;
392
+
393
+ // Event Listeners
394
+ fileInput.addEventListener('change', handleFileSelect);
395
+ uploadArea.addEventListener('dragover', handleDragOver);
396
+ uploadArea.addEventListener('dragleave', handleDragLeave);
397
+ uploadArea.addEventListener('drop', handleDrop);
398
+ processBtn.addEventListener('click', startProcessing);
399
 
400
+ // Functions
401
+ function handleFileSelect() {
402
  if (this.files.length > 0) {
403
  fileInfo.textContent = this.files[0].name;
404
  uploadArea.style.borderColor = '#4361ee';
405
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
406
+ processBtn.disabled = false;
407
+
408
+ // Hide any previous results
409
+ resultContainer.style.display = 'none';
410
  }
411
+ }
412
 
413
+ function handleDragOver(e) {
 
414
  e.preventDefault();
415
  uploadArea.style.borderColor = '#4361ee';
416
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.1)';
417
+ }
418
 
419
+ function handleDragLeave() {
420
  uploadArea.style.borderColor = fileInput.files.length > 0 ? '#4361ee' : '#4895ef';
421
+ uploadArea.style.backgroundColor = fileInput.files.length > 0
422
+ ? 'rgba(67, 97, 238, 0.05)'
423
+ : 'transparent';
424
+ }
425
 
426
+ function handleDrop(e) {
427
  e.preventDefault();
428
  uploadArea.style.borderColor = '#4361ee';
429
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
 
431
  if (e.dataTransfer.files.length) {
432
  fileInput.files = e.dataTransfer.files;
433
  fileInfo.textContent = e.dataTransfer.files[0].name;
434
+ processBtn.disabled = false;
435
+
436
+ // Hide any previous results
437
+ resultContainer.style.display = 'none';
438
  }
439
+ }
440
 
441
+ function startProcessing() {
 
 
 
442
  if (!fileInput.files.length) {
443
+ alert('Please select a video file first.');
444
  return;
445
  }
446
 
447
+ // Get processing options
448
+ const voice = document.querySelector('input[name="voice"]:checked').value;
449
+ const cheerful = document.getElementById('tone-option').checked;
450
+
451
+ // Prepare form data
452
+ const formData = new FormData();
453
+ formData.append('video', fileInput.files[0]);
454
+ formData.append('voice', voice);
455
+ formData.append('cheerful', cheerful);
456
+
457
+ // Show progress UI
458
+ uploadContainer.style.display = 'none';
459
+ progressContainer.style.display = 'block';
460
+ progressFill.style.width = '0%';
461
+ progressMessage.textContent = 'Preparing to process your video...';
462
+
463
+ // Start processing
464
+ fetch('/upload', {
465
+ method: 'POST',
466
+ body: formData
467
+ })
468
+ .then(response => response.json())
469
+ .then(data => {
470
+ if (data.error) {
471
+ throw new Error(data.error);
472
+ }
473
+ currentTaskId = data.task_id;
474
+ startStatusChecking();
475
+ })
476
+ .catch(error => {
477
+ showError(error.message);
478
+ });
479
+ }
480
+
481
+ function startStatusChecking() {
482
+ // Clear any existing interval
483
+ if (statusCheckInterval) {
484
+ clearInterval(statusCheckInterval);
485
+ }
486
 
487
+ // Check status every 3 seconds
488
+ statusCheckInterval = setInterval(() => {
489
+ fetch(`/status/${currentTaskId}`)
490
+ .then(response => response.json())
491
+ .then(updateStatus)
492
+ .catch(error => {
493
+ console.error('Status check failed:', error);
494
+ });
495
+ }, 3000);
496
+ }
497
+
498
+ function updateStatus(status) {
499
+ if (status.error) {
500
+ showError(status.error);
501
+ clearInterval(statusCheckInterval);
502
+ return;
503
+ }
504
 
505
+ // Update progress
506
+ progressFill.style.width = `${status.progress}%`;
507
+ progressMessage.textContent = status.message;
508
+
509
+ // Update ETA if available
510
+ if (status.eta) {
511
+ etaDisplay.textContent = `ETA: ${status.eta}`;
512
+ }
513
 
514
+ // Handle completion
515
+ if (status.status === 'complete') {
516
+ clearInterval(statusCheckInterval);
517
+ progressMessage.textContent = 'Processing complete!';
518
 
519
+ // Show results
520
+ resultVideo.src = status.result_url;
521
+ scriptContainer.textContent = status.script;
522
+ progressContainer.style.display = 'none';
523
+ resultContainer.style.display = 'block';
524
 
525
+ // Reset for next upload
526
+ setTimeout(() => {
527
+ uploadContainer.style.display = 'block';
528
+ fileInput.value = '';
529
+ fileInfo.textContent = 'No file selected';
530
+ processBtn.disabled = true;
531
+ uploadArea.style.borderColor = '#4895ef';
532
+ uploadArea.style.backgroundColor = 'transparent';
533
+ }, 5000);
534
+ } else if (status.status === 'error') {
535
+ showError(status.message);
536
+ clearInterval(statusCheckInterval);
537
  }
538
+ }
539
 
540
+ function showError(message) {
541
+ progressContainer.style.display = 'none';
542
+ uploadContainer.style.display = 'block';
543
+ alert(`Error: ${message}`);
 
 
 
544
  }
545
  </script>
546
  </body>