Athspi commited on
Commit
25df726
·
verified ·
1 Parent(s): b717f7d

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +168 -283
templates/index.html CHANGED
@@ -16,7 +16,6 @@
16
  --gray: #6c757d;
17
  --success: #4cc9f0;
18
  --error: #f72585;
19
- --warning: #ffc107;
20
  --border-radius: 12px;
21
  --shadow: 0 10px 30px rgba(0,0,0,0.1);
22
  --transition: all 0.3s ease;
@@ -119,25 +118,38 @@
119
  font-weight: 600;
120
  margin-bottom: 0.8rem;
121
  color: var(--secondary);
 
 
 
122
  }
123
 
124
- select, .radio-group {
125
- width: 100%;
126
- padding: 0.8rem;
127
- border-radius: 8px;
128
- border: 1px solid #dee2e6;
129
- margin-bottom: 1rem;
130
- background: white;
131
  }
132
 
133
- .radio-option {
 
 
 
 
 
 
134
  display: flex;
135
  align-items: center;
136
- margin-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
137
  }
138
 
139
- .radio-option input {
140
- margin-right: 0.5rem;
141
  }
142
 
143
  .btn {
@@ -163,95 +175,97 @@
163
  box-shadow: 0 5px 15px rgba(63, 55, 201, 0.3);
164
  }
165
 
166
- .btn:disabled {
167
- background: var(--gray);
168
- cursor: not-allowed;
169
- transform: none;
170
- box-shadow: none;
171
  }
172
 
173
- .progress-container {
174
- margin: 2rem 0;
175
- display: none;
176
- }
177
-
178
- .progress-header {
179
  display: flex;
180
- justify-content: space-between;
181
- margin-bottom: 0.5rem;
182
  }
183
 
184
- .progress-bar {
185
- height: 10px;
186
- background: #e9ecef;
187
- border-radius: 5px;
188
- overflow: hidden;
189
  }
190
 
191
- .progress-fill {
192
- height: 100%;
193
- background: var(--primary);
194
- width: 0%;
195
- transition: width 0.3s ease;
196
  }
197
 
198
- .progress-details {
199
- margin-top: 1rem;
200
- font-size: 0.9rem;
201
- color: var(--gray);
 
 
202
  }
203
 
204
- .result-container {
205
- display: none;
206
- margin-top: 2rem;
207
- animation: fadeIn 0.5s ease;
 
 
 
208
  }
209
 
210
- @keyframes fadeIn {
211
- from { opacity: 0; }
212
- to { opacity: 1; }
213
  }
214
 
215
  .result-video {
216
  width: 100%;
217
  border-radius: var(--border-radius);
218
  margin-bottom: 1.5rem;
219
- aspect-ratio: 16/9;
220
  background: black;
221
  }
222
 
223
- .script-container {
224
  background: var(--dark);
225
  color: white;
226
  padding: 1.5rem;
227
  border-radius: var(--border-radius);
228
  max-height: 300px;
229
  overflow-y: auto;
 
230
  white-space: pre-wrap;
 
231
  }
232
 
233
- .notice {
234
- padding: 1rem;
235
- border-radius: var(--border-radius);
236
- margin-bottom: 1.5rem;
237
- display: flex;
238
- align-items: center;
239
- gap: 0.8rem;
240
  }
241
 
242
- .notice-api {
243
- background: rgba(67, 97, 238, 0.1);
244
- border-left: 4px solid var(--primary);
 
 
 
 
 
245
  }
246
 
247
- .notice-gtts {
248
- background: rgba(255, 193, 7, 0.1);
249
- border-left: 4px solid var(--warning);
250
  }
251
 
252
- .notice-error {
253
- background: rgba(247, 37, 133, 0.1);
254
- border-left: 4px solid var(--error);
255
  }
256
 
257
  @media (max-width: 768px) {
@@ -270,6 +284,11 @@
270
  .content {
271
  padding: 1.5rem;
272
  }
 
 
 
 
 
273
  }
274
  </style>
275
  </head>
@@ -277,170 +296,129 @@
277
  <div class="container">
278
  <header>
279
  <h1>AI Video Dubbing</h1>
280
- <p class="subtitle">Professional Video Dubbing with AI</p>
281
  </header>
282
 
283
  <div class="content">
284
- <div id="upload-container">
 
 
 
 
 
 
 
 
 
 
 
285
  <div class="upload-area" id="upload-area">
286
  <div class="upload-icon">
287
  <i class="fas fa-cloud-upload-alt"></i>
288
  </div>
289
  <h3>Drag & Drop Video File</h3>
290
- <p>Supported formats: MP4, MOV, WEBM, AVI</p>
291
  <div id="file-info" class="file-info">No file selected</div>
292
- <input type="file" id="video-input" class="file-input" accept="video/*">
293
  </div>
294
 
295
  <div class="options">
296
  <div class="option-group">
297
- <div class="option-title">Target Language</div>
298
- <select id="language-select">
299
- {% for language in languages %}
300
- <option value="{{ language }}"
301
- {% if language == default_language %}selected{% endif %}>
302
- {{ language }}
303
- </option>
304
- {% endfor %}
305
- </select>
306
- </div>
307
-
308
- <div class="option-group">
309
- <div class="option-title">Voice Type</div>
310
- <select id="voice-type">
311
- {% for voice_type in voice_types %}
312
- <option value="{{ voice_type }}">{{ voice_type }}</option>
313
  {% endfor %}
314
- </select>
315
  </div>
316
 
317
  <div class="option-group">
318
- <div class="option-title">TTS Provider</div>
319
- <div class="radio-group">
320
- <label class="radio-option">
321
- <input type="radio" name="tts_provider" value="gtts" checked>
322
- gTTS (Free)
323
- </label>
324
- {% if tts_api_available %}
325
- <label class="radio-option">
326
- <input type="radio" name="tts_provider" value="api">
327
- Premium TTS API
328
- </label>
329
- {% endif %}
330
  </div>
331
- {% if tts_api_available %}
332
- <div class="notice notice-api">
333
- <i class="fas fa-info-circle"></i>
334
- <div>
335
- <strong>Premium TTS API</strong>
336
- <p>Higher quality with expressive narration</p>
337
- </div>
338
- </div>
339
- {% else %}
340
- <div class="notice notice-gtts">
341
- <i class="fas fa-info-circle"></i>
342
- <div>
343
- <strong>gTTS</strong>
344
- <p>Basic text-to-speech with clean narration</p>
345
- </div>
346
  </div>
347
- {% endif %}
348
  </div>
349
  </div>
350
 
351
- <button id="process-btn" class="btn" disabled>
352
  <i class="fas fa-magic"></i>
353
  Generate Dubbed Video
354
  </button>
355
- </div>
356
 
357
- <div class="progress-container" id="progress-container">
358
- <div class="progress-header">
359
- <span>Processing Status</span>
360
- <span id="progress-percent">0%</span>
361
- </div>
362
- <div class="progress-bar">
363
- <div class="progress-fill" id="progress-fill"></div>
364
- </div>
365
- <div class="progress-details" id="progress-message">
366
- Preparing to process your video...
367
- </div>
368
  </div>
369
 
370
- <div class="result-container" id="result-container">
371
- <h2>Your Dubbed Video</h2>
372
- <video controls class="result-video" id="result-video">
 
 
 
 
 
373
  Your browser does not support the video tag.
374
  </video>
375
 
376
- <h2>Generated Script</h2>
377
- <div class="script-container" id="script-container"></div>
378
-
379
- <button id="new-video-btn" class="btn" style="margin-top: 1.5rem;">
380
- <i class="fas fa-redo"></i>
381
- Process Another Video
382
- </button>
383
  </div>
 
384
  </div>
385
  </div>
386
 
387
  <script>
388
- // DOM Elements
 
389
  const uploadArea = document.getElementById('upload-area');
390
- const fileInput = document.getElementById('video-input');
391
  const fileInfo = document.getElementById('file-info');
392
- const processBtn = document.getElementById('process-btn');
393
- const uploadContainer = document.getElementById('upload-container');
394
- const progressContainer = document.getElementById('progress-container');
395
- const progressFill = document.getElementById('progress-fill');
396
- const progressMessage = document.getElementById('progress-message');
397
- const progressPercent = document.getElementById('progress-percent');
398
- const resultContainer = document.getElementById('result-container');
399
- const resultVideo = document.getElementById('result-video');
400
- const scriptContainer = document.getElementById('script-container');
401
- const languageSelect = document.getElementById('language-select');
402
- const voiceTypeSelect = document.getElementById('voice-type');
403
- const newVideoBtn = document.getElementById('new-video-btn');
404
-
405
- // State
406
- let currentTaskId = null;
407
- let statusCheckInterval = null;
408
-
409
- // Event Listeners
410
- fileInput.addEventListener('change', handleFileSelect);
411
- uploadArea.addEventListener('dragover', handleDragOver);
412
- uploadArea.addEventListener('dragleave', handleDragLeave);
413
- uploadArea.addEventListener('drop', handleDrop);
414
- processBtn.addEventListener('click', startProcessing);
415
- newVideoBtn.addEventListener('click', resetForm);
416
-
417
- // Functions
418
- function handleFileSelect() {
419
  if (this.files.length > 0) {
420
  fileInfo.textContent = this.files[0].name;
421
  uploadArea.style.borderColor = '#4361ee';
422
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
423
- processBtn.disabled = false;
424
 
425
- // Hide previous results
426
- resultContainer.style.display = 'none';
 
 
427
  }
428
- }
429
 
430
- function handleDragOver(e) {
 
431
  e.preventDefault();
432
  uploadArea.style.borderColor = '#4361ee';
433
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.1)';
434
- }
435
 
436
- function handleDragLeave() {
437
  uploadArea.style.borderColor = fileInput.files.length > 0 ? '#4361ee' : '#4895ef';
438
- uploadArea.style.backgroundColor = fileInput.files.length > 0
439
- ? 'rgba(67, 97, 238, 0.05)'
440
- : 'transparent';
441
- }
442
 
443
- function handleDrop(e) {
444
  e.preventDefault();
445
  uploadArea.style.borderColor = '#4361ee';
446
  uploadArea.style.backgroundColor = 'rgba(67, 97, 238, 0.05)';
@@ -448,117 +426,24 @@
448
  if (e.dataTransfer.files.length) {
449
  fileInput.files = e.dataTransfer.files;
450
  fileInfo.textContent = e.dataTransfer.files[0].name;
451
- processBtn.disabled = false;
452
 
453
- // Hide previous results
454
- resultContainer.style.display = 'none';
455
- }
456
- }
457
-
458
- function startProcessing() {
459
- if (!fileInput.files.length) {
460
- alert('Please select a video file first');
461
- return;
462
- }
463
-
464
- // Get processing options
465
- const language = languageSelect.value;
466
- const voiceType = voiceTypeSelect.value;
467
- const ttsProvider = document.querySelector('input[name="tts_provider"]:checked').value;
468
-
469
- // Prepare form data
470
- const formData = new FormData();
471
- formData.append('video', fileInput.files[0]);
472
- formData.append('language', language);
473
- formData.append('voice_type', voiceType);
474
- formData.append('tts_provider', ttsProvider);
475
-
476
- // Show progress UI
477
- uploadContainer.style.display = 'none';
478
- progressContainer.style.display = 'block';
479
- progressFill.style.width = '0%';
480
- progressPercent.textContent = '0%';
481
- progressMessage.textContent = 'Starting processing...';
482
-
483
- // Start processing
484
- fetch('/upload', {
485
- method: 'POST',
486
- body: formData
487
- })
488
- .then(response => response.json())
489
- .then(data => {
490
- if (data.error) {
491
- throw new Error(data.error);
492
  }
493
- currentTaskId = data.task_id;
494
- startStatusChecking();
495
- })
496
- .catch(error => {
497
- showError(error.message);
498
- });
499
- }
500
-
501
- function startStatusChecking() {
502
- // Clear any existing interval
503
- if (statusCheckInterval) {
504
- clearInterval(statusCheckInterval);
505
- }
506
-
507
- // Check status every 2 seconds
508
- statusCheckInterval = setInterval(() => {
509
- fetch(`/status/${currentTaskId}`)
510
- .then(response => response.json())
511
- .then(updateStatus)
512
- .catch(error => {
513
- console.error('Status check failed:', error);
514
- });
515
- }, 2000);
516
- }
517
-
518
- function updateStatus(status) {
519
- if (status.error) {
520
- showError(status.error_details || status.message);
521
- return;
522
  }
 
523
 
524
- // Update progress
525
- const progress = status.progress || 0;
526
- progressFill.style.width = `${progress}%`;
527
- progressPercent.textContent = `${progress}%`;
528
- progressMessage.textContent = status.message;
529
-
530
- // Handle completion
531
- if (status.status === 'complete') {
532
- clearInterval(statusCheckInterval);
533
- progressContainer.style.display = 'none';
534
-
535
- // Show results
536
- resultVideo.src = status.result_url;
537
- scriptContainer.textContent = status.script;
538
- resultContainer.style.display = 'block';
539
  }
540
- }
541
-
542
- function showError(message) {
543
- alert(`Error: ${message}`);
544
- uploadContainer.style.display = 'block';
545
- progressContainer.style.display = 'none';
546
- resultContainer.style.display = 'none';
547
- }
548
-
549
- function resetForm() {
550
- // Reset all elements
551
- fileInput.value = '';
552
- fileInfo.textContent = 'No file selected';
553
- resultVideo.src = '';
554
- scriptContainer.textContent = '';
555
- resultContainer.style.display = 'none';
556
- uploadContainer.style.display = 'block';
557
- progressContainer.style.display = 'none';
558
- processBtn.disabled = true;
559
- uploadArea.style.borderColor = '#4895ef';
560
- uploadArea.style.backgroundColor = 'transparent';
561
- }
562
  </script>
563
  </body>
564
  </html>
 
16
  --gray: #6c757d;
17
  --success: #4cc9f0;
18
  --error: #f72585;
 
19
  --border-radius: 12px;
20
  --shadow: 0 10px 30px rgba(0,0,0,0.1);
21
  --transition: all 0.3s ease;
 
118
  font-weight: 600;
119
  margin-bottom: 0.8rem;
120
  color: var(--secondary);
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 0.5rem;
124
  }
125
 
126
+ .option-title i {
127
+ font-size: 1.1rem;
 
 
 
 
 
128
  }
129
 
130
+ .radio-group, .checkbox-group {
131
+ display: flex;
132
+ flex-wrap: wrap;
133
+ gap: 1rem;
134
+ }
135
+
136
+ .radio-option, .checkbox-option {
137
  display: flex;
138
  align-items: center;
139
+ gap: 0.5rem;
140
+ background: white;
141
+ padding: 0.8rem 1.2rem;
142
+ border-radius: 8px;
143
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
144
+ transition: var(--transition);
145
+ }
146
+
147
+ .radio-option:hover, .checkbox-option:hover {
148
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
149
  }
150
 
151
+ .radio-option input, .checkbox-option input {
152
+ accent-color: var(--primary);
153
  }
154
 
155
  .btn {
 
175
  box-shadow: 0 5px 15px rgba(63, 55, 201, 0.3);
176
  }
177
 
178
+ .btn i {
179
+ font-size: 1.2rem;
 
 
 
180
  }
181
 
182
+ .alert {
183
+ padding: 1rem;
184
+ border-radius: var(--border-radius);
185
+ margin-bottom: 1.5rem;
186
+ font-weight: 500;
 
187
  display: flex;
188
+ align-items: center;
189
+ gap: 0.8rem;
190
  }
191
 
192
+ .alert-success {
193
+ background: rgba(76, 201, 240, 0.2);
194
+ color: #0a9396;
195
+ border: 1px solid rgba(76, 201, 240, 0.3);
 
196
  }
197
 
198
+ .alert-error {
199
+ background: rgba(247, 37, 133, 0.1);
200
+ color: var(--error);
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) {
 
284
  .content {
285
  padding: 1.5rem;
286
  }
287
+
288
+ .radio-group, .checkbox-group {
289
+ flex-direction: column;
290
+ gap: 0.8rem;
291
+ }
292
  }
293
  </style>
294
  </head>
 
296
  <div class="container">
297
  <header>
298
  <h1>AI Video Dubbing</h1>
299
+ <p class="subtitle">Transform your videos with AI-powered Tamil dubbing</p>
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>
318
  </div>
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">
326
  <div class="option-group">
327
+ <div class="option-title">
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 %}>
335
+ {{ voice }}
336
+ </label>
 
 
 
 
 
 
337
  {% endfor %}
338
+ </div>
339
  </div>
340
 
341
  <div class="option-group">
342
+ <div class="option-title">
343
+ <i class="fas fa-adjust"></i>
344
+ Tone Options
 
 
 
 
 
 
 
 
 
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
  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>