Athspi commited on
Commit
ee4d359
·
verified ·
1 Parent(s): 1b5ea35

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +275 -101
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,36 +208,65 @@
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;
@@ -242,30 +278,13 @@
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,18 +319,7 @@
300
  </header>
301
 
302
  <div class="content">
303
- {% with messages = get_flashed_messages(with_categories=true) %}
304
- {% if messages %}
305
- {% for category, message in messages %}
306
- <div class="alert alert-{{ category }}">
307
- <i class="fas {% if category == 'success' %}fa-check-circle{% else %}fa-exclamation-circle{% endif %}"></i>
308
- {{ message }}
309
- </div>
310
- {% endfor %}
311
- {% endif %}
312
- {% endwith %}
313
-
314
- <form id="dubbing-form" action="/process" method="POST" enctype="multipart/form-data">
315
  <div class="upload-area" id="upload-area">
316
  <div class="upload-icon">
317
  <i class="fas fa-cloud-upload-alt"></i>
@@ -319,7 +327,7 @@
319
  <h3>Drag & Drop Video File</h3>
320
  <p>or click to browse (MP4, MOV, WEBM, AVI)</p>
321
  <div id="file-info" class="file-info">No file selected</div>
322
- <input type="file" id="video" name="video" class="file-input" accept="video/*">
323
  </div>
324
 
325
  <div class="options">
@@ -328,7 +336,7 @@
328
  <i class="fas fa-microphone"></i>
329
  Voice Style
330
  </div>
331
- <div class="radio-group">
332
  {% for voice, value in voices.items() %}
333
  <label class="radio-option">
334
  <input type="radio" name="voice" value="{{ value }}" {% if loop.first %}checked{% endif %}>
@@ -345,80 +353,122 @@
345
  </div>
346
  <div class="checkbox-group">
347
  <label class="checkbox-option">
348
- <input type="checkbox" name="tone">
349
  Cheerful Tone
350
  </label>
351
  </div>
352
  </div>
353
  </div>
354
 
355
- <button type="submit" class="btn">
356
  <i class="fas fa-magic"></i>
357
  Generate Dubbed Video
358
  </button>
359
- </form>
360
-
361
- <div class="loader" id="loader">
362
- <div class="spinner"></div>
363
- <p class="loading-text">Processing your video. Please wait...</p>
364
  </div>
365
 
366
- {% if result_video %}
367
- <div class="result-section" id="result-section" style="display: block;">
368
- <div class="result-title">
369
- <i class="fas fa-video"></i>
370
- Your Dubbed Video
371
  </div>
372
- <video controls class="result-video">
373
- <source src="{{ result_video }}" type="video/mp4">
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  Your browser does not support the video tag.
375
  </video>
376
 
377
- <div class="result-title">
378
- <i class="fas fa-scroll"></i>
379
- Generated Script
 
 
 
 
 
380
  </div>
381
- <div class="script-box">{{ script }}</div>
382
  </div>
383
- {% endif %}
384
  </div>
385
  </div>
386
 
387
  <script>
388
- const form = document.getElementById('dubbing-form');
389
- const loader = document.getElementById('loader');
390
  const uploadArea = document.getElementById('upload-area');
391
- const fileInput = document.getElementById('video');
392
  const fileInfo = document.getElementById('file-info');
393
- const resultSection = document.getElementById('result-section');
394
-
395
- // Handle file selection
396
- fileInput.addEventListener('change', function() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  if (this.files.length > 0) {
398
  fileInfo.textContent = this.files[0].name;
399
  uploadArea.style.borderColor = '#4361ee';
400
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
 
 
401
 
402
  // Hide any previous results
403
- if (resultSection) {
404
- resultSection.style.display = 'none';
405
- }
406
  }
407
- });
408
 
409
- // Drag and drop functionality
410
- uploadArea.addEventListener('dragover', (e) => {
411
  e.preventDefault();
412
  uploadArea.style.borderColor = '#4361ee';
413
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.1)';
414
- });
415
 
416
- uploadArea.addEventListener('dragleave', () => {
417
  uploadArea.style.borderColor = fileInput.files.length > 0 ? '#4361ee' : '#4895ef';
418
- uploadArea.style.backgroundColor = fileInput.files.length > 0 ? 'rgba(67, 97, 238, 0.05)' : 'transparent';
419
- });
 
 
420
 
421
- uploadArea.addEventListener('drop', (e) => {
422
  e.preventDefault();
423
  uploadArea.style.borderColor = '#4361ee';
424
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
@@ -426,24 +476,148 @@
426
  if (e.dataTransfer.files.length) {
427
  fileInput.files = e.dataTransfer.files;
428
  fileInfo.textContent = e.dataTransfer.files[0].name;
 
 
429
 
430
  // Hide any previous results
431
- if (resultSection) {
432
- resultSection.style.display = 'none';
433
- }
434
  }
435
- });
436
 
437
- // Form submission
438
- form.addEventListener('submit', function(e) {
439
  if (!fileInput.files.length) {
440
- e.preventDefault();
441
  alert('Please select a video file first.');
442
- } else {
443
- form.style.display = 'none';
444
- loader.style.display = 'block';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  }
446
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  </script>
448
  </body>
449
  </html>
 
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
+ .time-estimate {
243
+ margin-top: 0.5rem;
244
+ display: flex;
245
+ justify-content: space-between;
246
+ font-size: 0.85rem;
247
+ color: var(--gray);
248
+ }
249
+
250
+ .result-container {
251
+ display: none;
252
+ margin-top: 2rem;
253
+ animation: fadeIn 0.5s ease;
254
+ }
255
+
256
+ @keyframes fadeIn {
257
+ from { opacity: 0; }
258
+ to { opacity: 1; }
259
  }
260
 
261
  .result-video {
262
  width: 100%;
263
  border-radius: var(--border-radius);
264
  margin-bottom: 1.5rem;
265
+ aspect-ratio: 16/9;
266
  background: black;
267
  }
268
 
269
+ .script-container {
270
  background: var(--dark);
271
  color: white;
272
  padding: 1.5rem;
 
278
  line-height: 1.6;
279
  }
280
 
281
+ .duration-notice {
282
+ margin-top: 1rem;
283
+ padding: 0.8rem;
284
+ background: #fff8e1;
285
+ border-radius: 8px;
286
+ border-left: 4px solid #ffc107;
287
+ font-size: 0.9rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
 
290
  @media (max-width: 768px) {
 
319
  </header>
320
 
321
  <div class="content">
322
+ <div id="upload-container">
 
 
 
 
 
 
 
 
 
 
 
323
  <div class="upload-area" id="upload-area">
324
  <div class="upload-icon">
325
  <i class="fas fa-cloud-upload-alt"></i>
 
327
  <h3>Drag & Drop Video File</h3>
328
  <p>or click to browse (MP4, MOV, WEBM, AVI)</p>
329
  <div id="file-info" class="file-info">No file selected</div>
330
+ <input type="file" id="video-input" class="file-input" accept="video/*">
331
  </div>
332
 
333
  <div class="options">
 
336
  <i class="fas fa-microphone"></i>
337
  Voice Style
338
  </div>
339
+ <div class="radio-group" id="voice-options">
340
  {% for voice, value in voices.items() %}
341
  <label class="radio-option">
342
  <input type="radio" name="voice" value="{{ value }}" {% if loop.first %}checked{% endif %}>
 
353
  </div>
354
  <div class="checkbox-group">
355
  <label class="checkbox-option">
356
+ <input type="checkbox" name="tone" id="tone-option">
357
  Cheerful Tone
358
  </label>
359
  </div>
360
  </div>
361
  </div>
362
 
363
+ <button id="process-btn" class="btn" disabled>
364
  <i class="fas fa-magic"></i>
365
  Generate Dubbed Video
366
  </button>
367
+
368
+ <div id="duration-notice" class="duration-notice" style="display: none;">
369
+ <i class="fas fa-clock"></i>
370
+ Note: Processing time is approximately 1.5x the video duration
371
+ </div>
372
  </div>
373
 
374
+ <div class="progress-container" id="progress-container">
375
+ <div class="progress-header">
376
+ <span>Processing your video</span>
377
+ <span id="eta">Estimating time...</span>
 
378
  </div>
379
+ <div class="progress-bar">
380
+ <div class="progress-fill" id="progress-fill"></div>
381
+ </div>
382
+ <div class="progress-details" id="progress-message">
383
+ Preparing to process your video...
384
+ </div>
385
+ <div class="time-estimate">
386
+ <span id="elapsed-time">Elapsed: 0s</span>
387
+ <span id="remaining-time">Remaining: Calculating...</span>
388
+ </div>
389
+ </div>
390
+
391
+ <div class="result-container" id="result-container">
392
+ <h2>Your Dubbed Video</h2>
393
+ <video controls class="result-video" id="result-video">
394
  Your browser does not support the video tag.
395
  </video>
396
 
397
+ <h2>Generated Script</h2>
398
+ <div class="script-container" id="script-container"></div>
399
+
400
+ <div class="btn-container" style="margin-top: 1.5rem;">
401
+ <button id="new-video-btn" class="btn">
402
+ <i class="fas fa-sync-alt"></i>
403
+ Process Another Video
404
+ </button>
405
  </div>
 
406
  </div>
 
407
  </div>
408
  </div>
409
 
410
  <script>
411
+ // DOM Elements
 
412
  const uploadArea = document.getElementById('upload-area');
413
+ const fileInput = document.getElementById('video-input');
414
  const fileInfo = document.getElementById('file-info');
415
+ const processBtn = document.getElementById('process-btn');
416
+ const uploadContainer = document.getElementById('upload-container');
417
+ const progressContainer = document.getElementById('progress-container');
418
+ const progressFill = document.getElementById('progress-fill');
419
+ const progressMessage = document.getElementById('progress-message');
420
+ const etaDisplay = document.getElementById('eta');
421
+ const resultContainer = document.getElementById('result-container');
422
+ const resultVideo = document.getElementById('result-video');
423
+ const scriptContainer = document.getElementById('script-container');
424
+ const elapsedTimeDisplay = document.getElementById('elapsed-time');
425
+ const remainingTimeDisplay = document.getElementById('remaining-time');
426
+ const durationNotice = document.getElementById('duration-notice');
427
+ const newVideoBtn = document.getElementById('new-video-btn');
428
+
429
+ // State
430
+ let currentTaskId = null;
431
+ let statusCheckInterval = null;
432
+ let processingStartTime = 0;
433
+ let videoDuration = 0;
434
+ let elapsedTimer = null;
435
+
436
+ // Event Listeners
437
+ fileInput.addEventListener('change', handleFileSelect);
438
+ uploadArea.addEventListener('dragover', handleDragOver);
439
+ uploadArea.addEventListener('dragleave', handleDragLeave);
440
+ uploadArea.addEventListener('drop', handleDrop);
441
+ processBtn.addEventListener('click', startProcessing);
442
+ newVideoBtn.addEventListener('click', resetForm);
443
+
444
+ // Functions
445
+ function handleFileSelect() {
446
  if (this.files.length > 0) {
447
  fileInfo.textContent = this.files[0].name;
448
  uploadArea.style.borderColor = '#4361ee';
449
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
450
+ processBtn.disabled = false;
451
+ durationNotice.style.display = 'block';
452
 
453
  // Hide any previous results
454
+ resultContainer.style.display = 'none';
 
 
455
  }
456
+ }
457
 
458
+ function handleDragOver(e) {
 
459
  e.preventDefault();
460
  uploadArea.style.borderColor = '#4361ee';
461
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.1)';
462
+ }
463
 
464
+ function handleDragLeave() {
465
  uploadArea.style.borderColor = fileInput.files.length > 0 ? '#4361ee' : '#4895ef';
466
+ uploadArea.style.backgroundColor = fileInput.files.length > 0
467
+ ? 'rgba(67, 97, 238, 0.05)'
468
+ : 'transparent';
469
+ }
470
 
471
+ function handleDrop(e) {
472
  e.preventDefault();
473
  uploadArea.style.borderColor = '#4361ee';
474
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
 
476
  if (e.dataTransfer.files.length) {
477
  fileInput.files = e.dataTransfer.files;
478
  fileInfo.textContent = e.dataTransfer.files[0].name;
479
+ processBtn.disabled = false;
480
+ durationNotice.style.display = 'block';
481
 
482
  // Hide any previous results
483
+ resultContainer.style.display = 'none';
 
 
484
  }
485
+ }
486
 
487
+ function startProcessing() {
 
488
  if (!fileInput.files.length) {
 
489
  alert('Please select a video file first.');
490
+ return;
491
+ }
492
+
493
+ // Get processing options
494
+ const voice = document.querySelector('input[name="voice"]:checked').value;
495
+ const cheerful = document.getElementById('tone-option').checked;
496
+
497
+ // Prepare form data
498
+ const formData = new FormData();
499
+ formData.append('video', fileInput.files[0]);
500
+ formData.append('voice', voice);
501
+ formData.append('cheerful', cheerful);
502
+
503
+ // Show progress UI
504
+ uploadContainer.style.display = 'none';
505
+ progressContainer.style.display = 'block';
506
+ progressFill.style.width = '0%';
507
+ progressMessage.textContent = 'Preparing to process your video...';
508
+ processingStartTime = Date.now();
509
+
510
+ // Start elapsed timer
511
+ startElapsedTimer();
512
+
513
+ // Start processing
514
+ fetch('/upload', {
515
+ method: 'POST',
516
+ body: formData
517
+ })
518
+ .then(response => response.json())
519
+ .then(data => {
520
+ if (data.error) {
521
+ throw new Error(data.error);
522
+ }
523
+ currentTaskId = data.task_id;
524
+ videoDuration = data.video_duration || 0;
525
+ startStatusChecking();
526
+ })
527
+ .catch(error => {
528
+ showError(error.message);
529
+ });
530
+ }
531
+
532
+ function startStatusChecking() {
533
+ // Clear any existing interval
534
+ if (statusCheckInterval) {
535
+ clearInterval(statusCheckInterval);
536
+ }
537
+
538
+ // Check status every 3 seconds
539
+ statusCheckInterval = setInterval(() => {
540
+ fetch(`/status/${currentTaskId}`)
541
+ .then(response => response.json())
542
+ .then(updateStatus)
543
+ .catch(error => {
544
+ console.error('Status check failed:', error);
545
+ });
546
+ }, 3000);
547
+ }
548
+
549
+ function startElapsedTimer() {
550
+ if (elapsedTimer) {
551
+ clearInterval(elapsedTimer);
552
+ }
553
+
554
+ elapsedTimer = setInterval(() => {
555
+ const elapsedSeconds = Math.floor((Date.now() - processingStartTime) / 1000);
556
+ elapsedTimeDisplay.textContent = `Elapsed: ${formatTime(elapsedSeconds)}`;
557
+ }, 1000);
558
+ }
559
+
560
+ function formatTime(seconds) {
561
+ const mins = Math.floor(seconds / 60);
562
+ const secs = seconds % 60;
563
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
564
+ }
565
+
566
+ function updateStatus(status) {
567
+ if (status.error) {
568
+ showError(status.error);
569
+ clearInterval(statusCheckInterval);
570
+ return;
571
  }
572
+
573
+ // Update progress
574
+ progressFill.style.width = `${status.progress}%`;
575
+ progressMessage.textContent = status.message;
576
+
577
+ // Update ETA if available
578
+ if (status.eta) {
579
+ etaDisplay.textContent = `ETA: ${status.eta}`;
580
+ remainingTimeDisplay.textContent = `Remaining: ${status.eta}`;
581
+ }
582
+
583
+ // Handle completion
584
+ if (status.status === 'complete') {
585
+ clearInterval(statusCheckInterval);
586
+ clearInterval(elapsedTimer);
587
+ progressMessage.textContent = 'Processing complete!';
588
+
589
+ // Show results
590
+ resultVideo.src = status.result_url;
591
+ scriptContainer.textContent = status.script;
592
+ progressContainer.style.display = 'none';
593
+ resultContainer.style.display = 'block';
594
+ } else if (status.status === 'error') {
595
+ showError(status.message);
596
+ clearInterval(statusCheckInterval);
597
+ clearInterval(elapsedTimer);
598
+ }
599
+ }
600
+
601
+ function showError(message) {
602
+ progressContainer.style.display = 'none';
603
+ uploadContainer.style.display = 'block';
604
+ alert(`Error: ${message}`);
605
+ clearInterval(elapsedTimer);
606
+ }
607
+
608
+ function resetForm() {
609
+ // Reset all elements
610
+ fileInput.value = '';
611
+ fileInfo.textContent = 'No file selected';
612
+ resultVideo.src = '';
613
+ scriptContainer.textContent = '';
614
+ resultContainer.style.display = 'none';
615
+ uploadContainer.style.display = 'block';
616
+ processBtn.disabled = true;
617
+ uploadArea.style.borderColor = '#4895ef';
618
+ uploadArea.style.backgroundColor = 'transparent';
619
+ durationNotice.style.display = 'none';
620
+ }
621
  </script>
622
  </body>
623
  </html>