KEXEL commited on
Commit
03c264f
·
verified ·
1 Parent(s): 295879d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +161 -19
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>MP4 to TS Converter | Real Video Conversion</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script src="https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script>
@@ -31,13 +31,38 @@
31
  .settings-panel.open {
32
  max-height: 500px;
33
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  </style>
35
  </head>
36
  <body class="bg-gray-50 min-h-screen">
37
  <div class="container mx-auto px-4 py-8">
38
  <header class="text-center mb-12">
39
- <h1 class="text-4xl font-bold text-blue-600 mb-2">MP4 to TS Converter</h1>
40
- <p class="text-gray-600 max-w-2xl mx-auto">Convert your MP4 videos to TS (MPEG Transport Stream) format using browser-based FFmpeg</p>
41
  </header>
42
 
43
  <main class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden p-6">
@@ -62,6 +87,8 @@
62
  <div>
63
  <h3 id="fileName" class="font-medium text-gray-800"></h3>
64
  <p id="fileSize" class="text-sm text-gray-500"></p>
 
 
65
  </div>
66
  </div>
67
  <button id="removeFile" class="text-red-500 hover:text-red-700">
@@ -81,15 +108,31 @@
81
  <!-- Conversion Settings -->
82
  <div class="mb-6">
83
  <div class="flex items-center justify-between cursor-pointer" id="settingsToggle">
84
- <h3 class="text-lg font-medium text-gray-700">Conversion Settings</h3>
85
  <i class="fas fa-chevron-down text-gray-500 transition-transform" id="settingsIcon"></i>
86
  </div>
87
  <div class="settings-panel mt-3" id="settingsPanel">
88
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
 
 
 
 
 
 
 
 
89
  <div>
90
  <label class="block text-sm font-medium text-gray-700 mb-1">Video Bitrate (kbps)</label>
91
  <input type="number" id="videoBitrate" value="2000" min="500" max="20000" class="w-full p-2 border border-gray-300 rounded">
92
  </div>
 
 
 
 
 
 
 
 
93
  <div>
94
  <label class="block text-sm font-medium text-gray-700 mb-1">Audio Bitrate (kbps)</label>
95
  <input type="number" id="audioBitrate" value="128" min="64" max="320" class="w-full p-2 border border-gray-300 rounded">
@@ -119,6 +162,12 @@
119
  <div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
120
  </div>
121
  <div id="statusMessage" class="text-sm text-gray-500 mt-2"></div>
 
 
 
 
 
 
122
  </div>
123
 
124
  <!-- Convert Button -->
@@ -151,7 +200,7 @@
151
  </main>
152
 
153
  <footer class="mt-12 text-center text-gray-500 text-sm">
154
- <p>MP4 to TS Converter - Uses FFmpeg.js for in-browser conversion. No files are uploaded to any server.</p>
155
  <p class="mt-2">Note: First conversion may take longer as FFmpeg needs to load (~30MB).</p>
156
  <p class="mt-2">© 2023 Video Converter Tool. All rights reserved.</p>
157
  </footer>
@@ -166,6 +215,8 @@
166
  const fileInfo = document.getElementById('fileInfo');
167
  const fileName = document.getElementById('fileName');
168
  const fileSize = document.getElementById('fileSize');
 
 
169
  const removeFile = document.getElementById('removeFile');
170
  const videoPreviewContainer = document.getElementById('videoPreviewContainer');
171
  const videoPreview = document.getElementById('videoPreview');
@@ -175,6 +226,8 @@
175
  const progressBar = document.getElementById('progressBar');
176
  const progressPercent = document.getElementById('progressPercent');
177
  const statusMessage = document.getElementById('statusMessage');
 
 
178
  const downloadSection = document.getElementById('downloadSection');
179
  const downloadBtn = document.getElementById('downloadBtn');
180
  const settingsToggle = document.getElementById('settingsToggle');
@@ -185,6 +238,7 @@
185
  const { createFFmpeg, fetchFile } = FFmpeg;
186
  const ffmpeg = createFFmpeg({
187
  log: true,
 
188
  progress: ({ ratio }) => {
189
  // 0 to 1 => 0 to 100
190
  const percent = Math.round(ratio * 100);
@@ -193,8 +247,20 @@
193
  }
194
  });
195
 
 
 
 
 
 
 
 
 
 
 
 
196
  let selectedFile = null;
197
  let convertedBlob = null;
 
198
 
199
  // Toggle settings panel
200
  settingsToggle.addEventListener('click', function() {
@@ -251,7 +317,7 @@
251
  // Handle file selection
252
  function handleFileSelection(file) {
253
  if (!file.type.match('video/mp4') && !file.name.match(/\.mp4$/i)) {
254
- alert('Please select an MP4 video file.');
255
  return;
256
  }
257
 
@@ -259,18 +325,43 @@
259
 
260
  // Display file info
261
  fileName.textContent = file.name;
262
- fileSize.textContent = formatFileSize(file.size);
263
  fileInfo.classList.remove('hidden');
264
 
265
  // Enable convert button
266
  convertBtn.disabled = false;
267
 
268
  // Show video preview
269
- videoPreview.src = URL.createObjectURL(file);
 
270
  videoPreviewContainer.classList.remove('hidden');
271
 
272
  // Hide dropzone
273
  dropzone.classList.add('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  }
275
 
276
  // Remove file
@@ -291,9 +382,11 @@
291
  convertBtn.addEventListener('click', async function() {
292
  if (!selectedFile) return;
293
 
294
- // Show progress
295
  progressContainer.classList.remove('hidden');
296
- statusMessage.textContent = "Loading FFmpeg...";
 
 
297
 
298
  // Disable convert button and show spinner
299
  convertBtn.disabled = true;
@@ -302,23 +395,27 @@
302
  try {
303
  // Load FFmpeg if not already loaded
304
  if (!ffmpeg.isLoaded()) {
 
305
  await ffmpeg.load();
 
306
  }
307
 
308
- statusMessage.textContent = "Reading input file...";
309
 
310
  // Write the file to FFmpeg's virtual file system
311
  const inputName = 'input.mp4';
312
  ffmpeg.FS('writeFile', inputName, await fetchFile(selectedFile));
313
 
314
  // Get conversion settings
 
315
  const videoBitrate = document.getElementById('videoBitrate').value;
 
316
  const audioBitrate = document.getElementById('audioBitrate').value;
317
  const segmentDuration = document.getElementById('segmentDuration').value;
318
  const outputFormat = document.getElementById('outputFormat').value;
319
 
320
  // Run FFmpeg command
321
- statusMessage.textContent = "Converting video...";
322
 
323
  let outputName, command;
324
 
@@ -326,9 +423,9 @@
326
  outputName = 'output.m3u8';
327
  command = [
328
  '-i', inputName,
329
- '-c:v', 'libx264',
330
  '-b:v', `${videoBitrate}k`,
331
- '-c:a', 'aac',
332
  '-b:a', `${audioBitrate}k`,
333
  '-hls_time', segmentDuration,
334
  '-hls_playlist_type', 'vod',
@@ -339,42 +436,80 @@
339
  outputName = 'output.ts';
340
  command = [
341
  '-i', inputName,
342
- '-c:v', 'libx264',
343
  '-b:v', `${videoBitrate}k`,
344
- '-c:a', 'aac',
345
  '-b:a', `${audioBitrate}k`,
346
  '-f', 'mpegts',
347
  outputName
348
  ];
349
  }
350
 
 
 
351
  await ffmpeg.run(...command);
352
 
353
  // Read the result
354
- statusMessage.textContent = "Finalizing output...";
355
  const data = ffmpeg.FS('readFile', outputName);
356
 
357
  // Create download blob
358
- convertedBlob = new Blob([data.buffer], { type: outputFormat === 'm3u8' ? 'application/x-mpegURL' : 'video/mp2t' });
 
 
359
 
360
  // Complete the process
361
  conversionComplete();
362
 
363
  } catch (error) {
364
  console.error('Conversion error:', error);
 
365
  statusMessage.textContent = `Error: ${error.message}`;
366
  convertSpinner.classList.add('hidden');
367
  convertBtn.disabled = false;
368
  }
369
  });
370
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  function updateProgress(percent) {
372
  progressBar.style.width = percent + '%';
373
  progressPercent.textContent = Math.round(percent) + '%';
374
  }
375
 
376
  function conversionComplete() {
377
- // Hide progress and spinner
378
  convertSpinner.classList.add('hidden');
379
 
380
  // Show download section
@@ -382,6 +517,7 @@
382
 
383
  // Update status
384
  statusMessage.textContent = "Conversion completed successfully!";
 
385
  }
386
 
387
  // Download button
@@ -411,11 +547,13 @@
411
  function resetConverter() {
412
  selectedFile = null;
413
  convertedBlob = null;
 
414
 
415
  // Reset UI
416
  fileInfo.classList.add('hidden');
417
  videoPreviewContainer.classList.add('hidden');
418
  progressContainer.classList.add('hidden');
 
419
  downloadSection.classList.add('hidden');
420
  convertBtn.disabled = true;
421
  convertSpinner.classList.add('hidden');
@@ -434,6 +572,10 @@
434
  URL.revokeObjectURL(videoPreview.src);
435
  videoPreview.src = '';
436
  }
 
 
 
 
437
  }
438
  });
439
  </script>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MP4 to TS Converter | Advanced Video Conversion</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script src="https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script>
 
31
  .settings-panel.open {
32
  max-height: 500px;
33
  }
34
+ .conversion-log {
35
+ max-height: 200px;
36
+ overflow-y: auto;
37
+ font-family: monospace;
38
+ background-color: #1e293b;
39
+ color: #f8fafc;
40
+ padding: 1rem;
41
+ border-radius: 0.5rem;
42
+ font-size: 0.875rem;
43
+ }
44
+ .log-entry {
45
+ margin-bottom: 0.25rem;
46
+ }
47
+ .log-info {
48
+ color: #60a5fa;
49
+ }
50
+ .log-warning {
51
+ color: #fbbf24;
52
+ }
53
+ .log-error {
54
+ color: #f87171;
55
+ }
56
+ .log-success {
57
+ color: #4ade80;
58
+ }
59
  </style>
60
  </head>
61
  <body class="bg-gray-50 min-h-screen">
62
  <div class="container mx-auto px-4 py-8">
63
  <header class="text-center mb-12">
64
+ <h1 class="text-4xl font-bold text-blue-600 mb-2">Advanced MP4 to TS Converter</h1>
65
+ <p class="text-gray-600 max-w-2xl mx-auto">Convert MP4 videos to TS format with full control over conversion parameters</p>
66
  </header>
67
 
68
  <main class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden p-6">
 
87
  <div>
88
  <h3 id="fileName" class="font-medium text-gray-800"></h3>
89
  <p id="fileSize" class="text-sm text-gray-500"></p>
90
+ <p id="fileDuration" class="text-sm text-gray-500"></p>
91
+ <p id="fileResolution" class="text-sm text-gray-500"></p>
92
  </div>
93
  </div>
94
  <button id="removeFile" class="text-red-500 hover:text-red-700">
 
108
  <!-- Conversion Settings -->
109
  <div class="mb-6">
110
  <div class="flex items-center justify-between cursor-pointer" id="settingsToggle">
111
+ <h3 class="text-lg font-medium text-gray-700">Advanced Conversion Settings</h3>
112
  <i class="fas fa-chevron-down text-gray-500 transition-transform" id="settingsIcon"></i>
113
  </div>
114
  <div class="settings-panel mt-3" id="settingsPanel">
115
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
116
+ <div>
117
+ <label class="block text-sm font-medium text-gray-700 mb-1">Video Codec</label>
118
+ <select id="videoCodec" class="w-full p-2 border border-gray-300 rounded">
119
+ <option value="libx264">H.264 (libx264)</option>
120
+ <option value="libx265">H.265 (libx265)</option>
121
+ <option value="mpeg2video">MPEG-2</option>
122
+ </select>
123
+ </div>
124
  <div>
125
  <label class="block text-sm font-medium text-gray-700 mb-1">Video Bitrate (kbps)</label>
126
  <input type="number" id="videoBitrate" value="2000" min="500" max="20000" class="w-full p-2 border border-gray-300 rounded">
127
  </div>
128
+ <div>
129
+ <label class="block text-sm font-medium text-gray-700 mb-1">Audio Codec</label>
130
+ <select id="audioCodec" class="w-full p-2 border border-gray-300 rounded">
131
+ <option value="aac">AAC</option>
132
+ <option value="mp3">MP3</option>
133
+ <option value="ac3">AC3</option>
134
+ </select>
135
+ </div>
136
  <div>
137
  <label class="block text-sm font-medium text-gray-700 mb-1">Audio Bitrate (kbps)</label>
138
  <input type="number" id="audioBitrate" value="128" min="64" max="320" class="w-full p-2 border border-gray-300 rounded">
 
162
  <div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
163
  </div>
164
  <div id="statusMessage" class="text-sm text-gray-500 mt-2"></div>
165
+
166
+ <!-- Conversion Log -->
167
+ <div id="conversionLogContainer" class="mt-4 hidden">
168
+ <h4 class="text-sm font-medium text-gray-700 mb-2">Conversion Log</h4>
169
+ <div id="conversionLog" class="conversion-log"></div>
170
+ </div>
171
  </div>
172
 
173
  <!-- Convert Button -->
 
200
  </main>
201
 
202
  <footer class="mt-12 text-center text-gray-500 text-sm">
203
+ <p>Advanced MP4 to TS Converter - Uses FFmpeg.wasm for in-browser conversion. No files are uploaded to any server.</p>
204
  <p class="mt-2">Note: First conversion may take longer as FFmpeg needs to load (~30MB).</p>
205
  <p class="mt-2">© 2023 Video Converter Tool. All rights reserved.</p>
206
  </footer>
 
215
  const fileInfo = document.getElementById('fileInfo');
216
  const fileName = document.getElementById('fileName');
217
  const fileSize = document.getElementById('fileSize');
218
+ const fileDuration = document.getElementById('fileDuration');
219
+ const fileResolution = document.getElementById('fileResolution');
220
  const removeFile = document.getElementById('removeFile');
221
  const videoPreviewContainer = document.getElementById('videoPreviewContainer');
222
  const videoPreview = document.getElementById('videoPreview');
 
226
  const progressBar = document.getElementById('progressBar');
227
  const progressPercent = document.getElementById('progressPercent');
228
  const statusMessage = document.getElementById('statusMessage');
229
+ const conversionLogContainer = document.getElementById('conversionLogContainer');
230
+ const conversionLog = document.getElementById('conversionLog');
231
  const downloadSection = document.getElementById('downloadSection');
232
  const downloadBtn = document.getElementById('downloadBtn');
233
  const settingsToggle = document.getElementById('settingsToggle');
 
238
  const { createFFmpeg, fetchFile } = FFmpeg;
239
  const ffmpeg = createFFmpeg({
240
  log: true,
241
+ corePath: 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js',
242
  progress: ({ ratio }) => {
243
  // 0 to 1 => 0 to 100
244
  const percent = Math.round(ratio * 100);
 
247
  }
248
  });
249
 
250
+ // Capture FFmpeg logs
251
+ ffmpeg.setLogger(({ type, message }) => {
252
+ if (!conversionLogContainer.classList.contains('hidden')) {
253
+ const logEntry = document.createElement('div');
254
+ logEntry.className = `log-entry log-${type}`;
255
+ logEntry.textContent = message;
256
+ conversionLog.appendChild(logEntry);
257
+ conversionLog.scrollTop = conversionLog.scrollHeight;
258
+ }
259
+ });
260
+
261
  let selectedFile = null;
262
  let convertedBlob = null;
263
+ let videoMetadata = {};
264
 
265
  // Toggle settings panel
266
  settingsToggle.addEventListener('click', function() {
 
317
  // Handle file selection
318
  function handleFileSelection(file) {
319
  if (!file.type.match('video/mp4') && !file.name.match(/\.mp4$/i)) {
320
+ showError('Please select an MP4 video file.');
321
  return;
322
  }
323
 
 
325
 
326
  // Display file info
327
  fileName.textContent = file.name;
328
+ fileSize.textContent = `Size: ${formatFileSize(file.size)}`;
329
  fileInfo.classList.remove('hidden');
330
 
331
  // Enable convert button
332
  convertBtn.disabled = false;
333
 
334
  // Show video preview
335
+ const videoURL = URL.createObjectURL(file);
336
+ videoPreview.src = videoURL;
337
  videoPreviewContainer.classList.remove('hidden');
338
 
339
  // Hide dropzone
340
  dropzone.classList.add('hidden');
341
+
342
+ // Extract video metadata
343
+ extractVideoMetadata(videoURL);
344
+ }
345
+
346
+ // Extract video metadata (duration, resolution)
347
+ function extractVideoMetadata(videoURL) {
348
+ videoPreview.onloadedmetadata = function() {
349
+ videoMetadata = {
350
+ duration: videoPreview.duration,
351
+ width: videoPreview.videoWidth,
352
+ height: videoPreview.videoHeight
353
+ };
354
+
355
+ fileDuration.textContent = `Duration: ${formatDuration(videoMetadata.duration)}`;
356
+ fileResolution.textContent = `Resolution: ${videoMetadata.width}x${videoMetadata.height}`;
357
+ };
358
+ }
359
+
360
+ // Format duration (seconds to HH:MM:SS)
361
+ function formatDuration(seconds) {
362
+ const date = new Date(0);
363
+ date.setSeconds(seconds);
364
+ return date.toISOString().substr(11, 8);
365
  }
366
 
367
  // Remove file
 
382
  convertBtn.addEventListener('click', async function() {
383
  if (!selectedFile) return;
384
 
385
+ // Show progress and log
386
  progressContainer.classList.remove('hidden');
387
+ conversionLogContainer.classList.remove('hidden');
388
+ conversionLog.innerHTML = '';
389
+ statusMessage.textContent = "Initializing conversion...";
390
 
391
  // Disable convert button and show spinner
392
  convertBtn.disabled = true;
 
395
  try {
396
  // Load FFmpeg if not already loaded
397
  if (!ffmpeg.isLoaded()) {
398
+ addLogEntry('Loading FFmpeg core (this may take a while on first run)...', 'info');
399
  await ffmpeg.load();
400
+ addLogEntry('FFmpeg loaded successfully', 'success');
401
  }
402
 
403
+ addLogEntry('Reading input file...', 'info');
404
 
405
  // Write the file to FFmpeg's virtual file system
406
  const inputName = 'input.mp4';
407
  ffmpeg.FS('writeFile', inputName, await fetchFile(selectedFile));
408
 
409
  // Get conversion settings
410
+ const videoCodec = document.getElementById('videoCodec').value;
411
  const videoBitrate = document.getElementById('videoBitrate').value;
412
+ const audioCodec = document.getElementById('audioCodec').value;
413
  const audioBitrate = document.getElementById('audioBitrate').value;
414
  const segmentDuration = document.getElementById('segmentDuration').value;
415
  const outputFormat = document.getElementById('outputFormat').value;
416
 
417
  // Run FFmpeg command
418
+ addLogEntry('Starting video conversion...', 'info');
419
 
420
  let outputName, command;
421
 
 
423
  outputName = 'output.m3u8';
424
  command = [
425
  '-i', inputName,
426
+ '-c:v', videoCodec,
427
  '-b:v', `${videoBitrate}k`,
428
+ '-c:a', audioCodec,
429
  '-b:a', `${audioBitrate}k`,
430
  '-hls_time', segmentDuration,
431
  '-hls_playlist_type', 'vod',
 
436
  outputName = 'output.ts';
437
  command = [
438
  '-i', inputName,
439
+ '-c:v', videoCodec,
440
  '-b:v', `${videoBitrate}k`,
441
+ '-c:a', audioCodec,
442
  '-b:a', `${audioBitrate}k`,
443
  '-f', 'mpegts',
444
  outputName
445
  ];
446
  }
447
 
448
+ addLogEntry(`Running FFmpeg command: ffmpeg ${command.join(' ')}`, 'info');
449
+
450
  await ffmpeg.run(...command);
451
 
452
  // Read the result
453
+ addLogEntry('Conversion complete, reading output file...', 'info');
454
  const data = ffmpeg.FS('readFile', outputName);
455
 
456
  // Create download blob
457
+ convertedBlob = new Blob([data.buffer], {
458
+ type: outputFormat === 'm3u8' ? 'application/x-mpegURL' : 'video/mp2t'
459
+ });
460
 
461
  // Complete the process
462
  conversionComplete();
463
 
464
  } catch (error) {
465
  console.error('Conversion error:', error);
466
+ addLogEntry(`Error: ${error.message}`, 'error');
467
  statusMessage.textContent = `Error: ${error.message}`;
468
  convertSpinner.classList.add('hidden');
469
  convertBtn.disabled = false;
470
  }
471
  });
472
 
473
+ // Add entry to conversion log
474
+ function addLogEntry(message, type = 'info') {
475
+ const logEntry = document.createElement('div');
476
+ logEntry.className = `log-entry log-${type}`;
477
+ logEntry.textContent = message;
478
+ conversionLog.appendChild(logEntry);
479
+ conversionLog.scrollTop = conversionLog.scrollHeight;
480
+ }
481
+
482
+ // Show error message
483
+ function showError(message) {
484
+ const errorDiv = document.createElement('div');
485
+ errorDiv.className = 'bg-red-50 border-l-4 border-red-500 p-4 mb-4';
486
+ errorDiv.innerHTML = `
487
+ <div class="flex">
488
+ <div class="flex-shrink-0">
489
+ <i class="fas fa-exclamation-circle text-red-500"></i>
490
+ </div>
491
+ <div class="ml-3">
492
+ <p class="text-sm text-red-700">${message}</p>
493
+ </div>
494
+ </div>
495
+ `;
496
+
497
+ // Insert after the dropzone
498
+ dropzone.parentNode.insertBefore(errorDiv, dropzone.nextSibling);
499
+
500
+ // Remove after 5 seconds
501
+ setTimeout(() => {
502
+ errorDiv.remove();
503
+ }, 5000);
504
+ }
505
+
506
  function updateProgress(percent) {
507
  progressBar.style.width = percent + '%';
508
  progressPercent.textContent = Math.round(percent) + '%';
509
  }
510
 
511
  function conversionComplete() {
512
+ // Hide spinner
513
  convertSpinner.classList.add('hidden');
514
 
515
  // Show download section
 
517
 
518
  // Update status
519
  statusMessage.textContent = "Conversion completed successfully!";
520
+ addLogEntry('Conversion process finished successfully', 'success');
521
  }
522
 
523
  // Download button
 
547
  function resetConverter() {
548
  selectedFile = null;
549
  convertedBlob = null;
550
+ videoMetadata = {};
551
 
552
  // Reset UI
553
  fileInfo.classList.add('hidden');
554
  videoPreviewContainer.classList.add('hidden');
555
  progressContainer.classList.add('hidden');
556
+ conversionLogContainer.classList.add('hidden');
557
  downloadSection.classList.add('hidden');
558
  convertBtn.disabled = true;
559
  convertSpinner.classList.add('hidden');
 
572
  URL.revokeObjectURL(videoPreview.src);
573
  videoPreview.src = '';
574
  }
575
+
576
+ // Clear metadata display
577
+ fileDuration.textContent = '';
578
+ fileResolution.textContent = '';
579
  }
580
  });
581
  </script>