|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>MP4 to TS Converter | Advanced Video Conversion</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<script src="https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script> |
|
|
<style> |
|
|
.dropzone { |
|
|
border: 2px dashed #3b82f6; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.dropzone.active { |
|
|
border-color: #10b981; |
|
|
background-color: #f0fdf4; |
|
|
} |
|
|
.progress-bar { |
|
|
transition: width 0.3s ease; |
|
|
} |
|
|
#videoPreview { |
|
|
max-width: 100%; |
|
|
max-height: 200px; |
|
|
} |
|
|
.settings-panel { |
|
|
transition: all 0.3s ease; |
|
|
max-height: 0; |
|
|
overflow: hidden; |
|
|
} |
|
|
.settings-panel.open { |
|
|
max-height: 500px; |
|
|
} |
|
|
.conversion-log { |
|
|
max-height: 200px; |
|
|
overflow-y: auto; |
|
|
font-family: monospace; |
|
|
background-color: #1e293b; |
|
|
color: #f8fafc; |
|
|
padding: 1rem; |
|
|
border-radius: 0.5rem; |
|
|
font-size: 0.875rem; |
|
|
} |
|
|
.log-entry { |
|
|
margin-bottom: 0.25rem; |
|
|
} |
|
|
.log-info { |
|
|
color: #60a5fa; |
|
|
} |
|
|
.log-warning { |
|
|
color: #fbbf24; |
|
|
} |
|
|
.log-error { |
|
|
color: #f87171; |
|
|
} |
|
|
.log-success { |
|
|
color: #4ade80; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 min-h-screen"> |
|
|
<div class="container mx-auto px-4 py-8"> |
|
|
<header class="text-center mb-12"> |
|
|
<h1 class="text-4xl font-bold text-blue-600 mb-2">Advanced MP4 to TS Converter</h1> |
|
|
<p class="text-gray-600 max-w-2xl mx-auto">Convert MP4 videos to TS format with full control over conversion parameters</p> |
|
|
</header> |
|
|
|
|
|
<main class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden p-6"> |
|
|
|
|
|
<div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-6"> |
|
|
<div class="flex flex-col items-center justify-center"> |
|
|
<i class="fas fa-file-video text-5xl text-blue-500 mb-4"></i> |
|
|
<h2 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop MP4 File Here</h2> |
|
|
<p class="text-gray-500 mb-4">or click to browse your files</p> |
|
|
<input type="file" id="fileInput" class="hidden" accept="video/mp4"> |
|
|
<button id="browseBtn" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition"> |
|
|
Select File |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="fileInfo" class="hidden mb-6"> |
|
|
<div class="flex items-center justify-between bg-gray-50 p-4 rounded-lg"> |
|
|
<div class="flex items-center"> |
|
|
<i class="fas fa-file-video text-2xl text-blue-500 mr-3"></i> |
|
|
<div> |
|
|
<h3 id="fileName" class="font-medium text-gray-800"></h3> |
|
|
<p id="fileSize" class="text-sm text-gray-500"></p> |
|
|
<p id="fileDuration" class="text-sm text-gray-500"></p> |
|
|
<p id="fileResolution" class="text-sm text-gray-500"></p> |
|
|
</div> |
|
|
</div> |
|
|
<button id="removeFile" class="text-red-500 hover:text-red-700"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="videoPreviewContainer" class="hidden mb-6"> |
|
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Video Preview</h3> |
|
|
<div class="flex justify-center bg-gray-100 rounded-lg p-4"> |
|
|
<video id="videoPreview" controls class="rounded"></video> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<div class="flex items-center justify-between cursor-pointer" id="settingsToggle"> |
|
|
<h3 class="text-lg font-medium text-gray-700">Advanced Conversion Settings</h3> |
|
|
<i class="fas fa-chevron-down text-gray-500 transition-transform" id="settingsIcon"></i> |
|
|
</div> |
|
|
<div class="settings-panel mt-3" id="settingsPanel"> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Video Codec</label> |
|
|
<select id="videoCodec" class="w-full p-2 border border-gray-300 rounded"> |
|
|
<option value="libx264">H.264 (libx264)</option> |
|
|
<option value="libx265">H.265 (libx265)</option> |
|
|
<option value="mpeg2video">MPEG-2</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Video Bitrate (kbps)</label> |
|
|
<input type="number" id="videoBitrate" value="2000" min="500" max="20000" class="w-full p-2 border border-gray-300 rounded"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Audio Codec</label> |
|
|
<select id="audioCodec" class="w-full p-2 border border-gray-300 rounded"> |
|
|
<option value="aac">AAC</option> |
|
|
<option value="mp3">MP3</option> |
|
|
<option value="ac3">AC3</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Audio Bitrate (kbps)</label> |
|
|
<input type="number" id="audioBitrate" value="128" min="64" max="320" class="w-full p-2 border border-gray-300 rounded"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Segment Duration (seconds)</label> |
|
|
<input type="number" id="segmentDuration" value="10" min="2" max="60" class="w-full p-2 border border-gray-300 rounded"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Output Format</label> |
|
|
<select id="outputFormat" class="w-full p-2 border border-gray-300 rounded"> |
|
|
<option value="ts">TS (Transport Stream)</option> |
|
|
<option value="m3u8">HLS (M3U8 Playlist)</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="progressContainer" class="hidden mb-6"> |
|
|
<div class="flex justify-between mb-1"> |
|
|
<span class="text-sm font-medium text-gray-700">Conversion Progress</span> |
|
|
<span id="progressPercent" class="text-sm font-medium text-gray-700">0%</span> |
|
|
</div> |
|
|
<div class="w-full bg-gray-200 rounded-full h-2.5"> |
|
|
<div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> |
|
|
</div> |
|
|
<div id="statusMessage" class="text-sm text-gray-500 mt-2"></div> |
|
|
|
|
|
|
|
|
<div id="conversionLogContainer" class="mt-4 hidden"> |
|
|
<h4 class="text-sm font-medium text-gray-700 mb-2">Conversion Log</h4> |
|
|
<div id="conversionLog" class="conversion-log"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="text-center"> |
|
|
<button id="convertBtn" class="bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-8 rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed" disabled> |
|
|
<i class="fas fa-cog animate-spin hidden mr-2" id="convertSpinner"></i> |
|
|
Convert to TS |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="downloadSection" class="hidden mt-8"> |
|
|
<div class="bg-green-50 border border-green-200 rounded-lg p-4"> |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<i class="fas fa-check-circle text-2xl text-green-500 mr-3"></i> |
|
|
<div> |
|
|
<h3 class="font-medium text-green-800">Conversion Complete!</h3> |
|
|
<p class="text-sm text-green-600">Your file is ready to download</p> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<button id="downloadBtn" class="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-6 rounded-lg transition"> |
|
|
<i class="fas fa-download mr-2"></i> Download |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
<footer class="mt-12 text-center text-gray-500 text-sm"> |
|
|
<p>Advanced MP4 to TS Converter - Uses FFmpeg.wasm for in-browser conversion. No files are uploaded to any server.</p> |
|
|
<p class="mt-2">Note: First conversion may take longer as FFmpeg needs to load (~30MB).</p> |
|
|
<p class="mt-2">© 2023 Video Converter Tool. All rights reserved.</p> |
|
|
</footer> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
const dropzone = document.getElementById('dropzone'); |
|
|
const fileInput = document.getElementById('fileInput'); |
|
|
const browseBtn = document.getElementById('browseBtn'); |
|
|
const fileInfo = document.getElementById('fileInfo'); |
|
|
const fileName = document.getElementById('fileName'); |
|
|
const fileSize = document.getElementById('fileSize'); |
|
|
const fileDuration = document.getElementById('fileDuration'); |
|
|
const fileResolution = document.getElementById('fileResolution'); |
|
|
const removeFile = document.getElementById('removeFile'); |
|
|
const videoPreviewContainer = document.getElementById('videoPreviewContainer'); |
|
|
const videoPreview = document.getElementById('videoPreview'); |
|
|
const convertBtn = document.getElementById('convertBtn'); |
|
|
const convertSpinner = document.getElementById('convertSpinner'); |
|
|
const progressContainer = document.getElementById('progressContainer'); |
|
|
const progressBar = document.getElementById('progressBar'); |
|
|
const progressPercent = document.getElementById('progressPercent'); |
|
|
const statusMessage = document.getElementById('statusMessage'); |
|
|
const conversionLogContainer = document.getElementById('conversionLogContainer'); |
|
|
const conversionLog = document.getElementById('conversionLog'); |
|
|
const downloadSection = document.getElementById('downloadSection'); |
|
|
const downloadBtn = document.getElementById('downloadBtn'); |
|
|
const settingsToggle = document.getElementById('settingsToggle'); |
|
|
const settingsPanel = document.getElementById('settingsPanel'); |
|
|
const settingsIcon = document.getElementById('settingsIcon'); |
|
|
|
|
|
|
|
|
const { createFFmpeg, fetchFile } = FFmpeg; |
|
|
const ffmpeg = createFFmpeg({ |
|
|
log: true, |
|
|
corePath: 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js', |
|
|
progress: ({ ratio }) => { |
|
|
|
|
|
const percent = Math.round(ratio * 100); |
|
|
updateProgress(percent); |
|
|
statusMessage.textContent = `Processing: ${percent}% complete`; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
ffmpeg.setLogger(({ type, message }) => { |
|
|
if (!conversionLogContainer.classList.contains('hidden')) { |
|
|
const logEntry = document.createElement('div'); |
|
|
logEntry.className = `log-entry log-${type}`; |
|
|
logEntry.textContent = message; |
|
|
conversionLog.appendChild(logEntry); |
|
|
conversionLog.scrollTop = conversionLog.scrollHeight; |
|
|
} |
|
|
}); |
|
|
|
|
|
let selectedFile = null; |
|
|
let convertedBlob = null; |
|
|
let videoMetadata = {}; |
|
|
|
|
|
|
|
|
settingsToggle.addEventListener('click', function() { |
|
|
settingsPanel.classList.toggle('open'); |
|
|
settingsIcon.classList.toggle('transform'); |
|
|
settingsIcon.classList.toggle('rotate-180'); |
|
|
}); |
|
|
|
|
|
|
|
|
browseBtn.addEventListener('click', function() { |
|
|
fileInput.click(); |
|
|
}); |
|
|
|
|
|
fileInput.addEventListener('change', function(e) { |
|
|
if (e.target.files.length > 0) { |
|
|
handleFileSelection(e.target.files[0]); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
|
dropzone.addEventListener(eventName, preventDefaults, false); |
|
|
}); |
|
|
|
|
|
function preventDefaults(e) { |
|
|
e.preventDefault(); |
|
|
e.stopPropagation(); |
|
|
} |
|
|
|
|
|
['dragenter', 'dragover'].forEach(eventName => { |
|
|
dropzone.addEventListener(eventName, highlight, false); |
|
|
}); |
|
|
|
|
|
['dragleave', 'drop'].forEach(eventName => { |
|
|
dropzone.addEventListener(eventName, unhighlight, false); |
|
|
}); |
|
|
|
|
|
function highlight() { |
|
|
dropzone.classList.add('active'); |
|
|
} |
|
|
|
|
|
function unhighlight() { |
|
|
dropzone.classList.remove('active'); |
|
|
} |
|
|
|
|
|
dropzone.addEventListener('drop', function(e) { |
|
|
const dt = e.dataTransfer; |
|
|
const file = dt.files[0]; |
|
|
if (file) { |
|
|
handleFileSelection(file); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function handleFileSelection(file) { |
|
|
if (!file.type.match('video/mp4') && !file.name.match(/\.mp4$/i)) { |
|
|
showError('Please select an MP4 video file.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
selectedFile = file; |
|
|
|
|
|
|
|
|
fileName.textContent = file.name; |
|
|
fileSize.textContent = `Size: ${formatFileSize(file.size)}`; |
|
|
fileInfo.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
convertBtn.disabled = false; |
|
|
|
|
|
|
|
|
const videoURL = URL.createObjectURL(file); |
|
|
videoPreview.src = videoURL; |
|
|
videoPreviewContainer.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
dropzone.classList.add('hidden'); |
|
|
|
|
|
|
|
|
extractVideoMetadata(videoURL); |
|
|
} |
|
|
|
|
|
|
|
|
function extractVideoMetadata(videoURL) { |
|
|
videoPreview.onloadedmetadata = function() { |
|
|
videoMetadata = { |
|
|
duration: videoPreview.duration, |
|
|
width: videoPreview.videoWidth, |
|
|
height: videoPreview.videoHeight |
|
|
}; |
|
|
|
|
|
fileDuration.textContent = `Duration: ${formatDuration(videoMetadata.duration)}`; |
|
|
fileResolution.textContent = `Resolution: ${videoMetadata.width}x${videoMetadata.height}`; |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
function formatDuration(seconds) { |
|
|
const date = new Date(0); |
|
|
date.setSeconds(seconds); |
|
|
return date.toISOString().substr(11, 8); |
|
|
} |
|
|
|
|
|
|
|
|
removeFile.addEventListener('click', function() { |
|
|
resetConverter(); |
|
|
}); |
|
|
|
|
|
|
|
|
function formatFileSize(bytes) { |
|
|
if (bytes === 0) return '0 Bytes'; |
|
|
const k = 1024; |
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']; |
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k)); |
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
|
|
} |
|
|
|
|
|
|
|
|
convertBtn.addEventListener('click', async function() { |
|
|
if (!selectedFile) return; |
|
|
|
|
|
|
|
|
progressContainer.classList.remove('hidden'); |
|
|
conversionLogContainer.classList.remove('hidden'); |
|
|
conversionLog.innerHTML = ''; |
|
|
statusMessage.textContent = "Initializing conversion..."; |
|
|
|
|
|
|
|
|
convertBtn.disabled = true; |
|
|
convertSpinner.classList.remove('hidden'); |
|
|
|
|
|
try { |
|
|
|
|
|
if (!ffmpeg.isLoaded()) { |
|
|
addLogEntry('Loading FFmpeg core (this may take a while on first run)...', 'info'); |
|
|
await ffmpeg.load(); |
|
|
addLogEntry('FFmpeg loaded successfully', 'success'); |
|
|
} |
|
|
|
|
|
addLogEntry('Reading input file...', 'info'); |
|
|
|
|
|
|
|
|
const inputName = 'input.mp4'; |
|
|
ffmpeg.FS('writeFile', inputName, await fetchFile(selectedFile)); |
|
|
|
|
|
|
|
|
const videoCodec = document.getElementById('videoCodec').value; |
|
|
const videoBitrate = document.getElementById('videoBitrate').value; |
|
|
const audioCodec = document.getElementById('audioCodec').value; |
|
|
const audioBitrate = document.getElementById('audioBitrate').value; |
|
|
const segmentDuration = document.getElementById('segmentDuration').value; |
|
|
const outputFormat = document.getElementById('outputFormat').value; |
|
|
|
|
|
|
|
|
addLogEntry('Starting video conversion...', 'info'); |
|
|
|
|
|
let outputName, command; |
|
|
|
|
|
if (outputFormat === 'm3u8') { |
|
|
outputName = 'output.m3u8'; |
|
|
command = [ |
|
|
'-i', inputName, |
|
|
'-c:v', videoCodec, |
|
|
'-b:v', `${videoBitrate}k`, |
|
|
'-c:a', audioCodec, |
|
|
'-b:a', `${audioBitrate}k`, |
|
|
'-hls_time', segmentDuration, |
|
|
'-hls_playlist_type', 'vod', |
|
|
'-f', 'hls', |
|
|
outputName |
|
|
]; |
|
|
} else { |
|
|
outputName = 'output.ts'; |
|
|
command = [ |
|
|
'-i', inputName, |
|
|
'-c:v', videoCodec, |
|
|
'-b:v', `${videoBitrate}k`, |
|
|
'-c:a', audioCodec, |
|
|
'-b:a', `${audioBitrate}k`, |
|
|
'-f', 'mpegts', |
|
|
outputName |
|
|
]; |
|
|
} |
|
|
|
|
|
addLogEntry(`Running FFmpeg command: ffmpeg ${command.join(' ')}`, 'info'); |
|
|
|
|
|
await ffmpeg.run(...command); |
|
|
|
|
|
|
|
|
addLogEntry('Conversion complete, reading output file...', 'info'); |
|
|
const data = ffmpeg.FS('readFile', outputName); |
|
|
|
|
|
|
|
|
convertedBlob = new Blob([data.buffer], { |
|
|
type: outputFormat === 'm3u8' ? 'application/x-mpegURL' : 'video/mp2t' |
|
|
}); |
|
|
|
|
|
|
|
|
conversionComplete(); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Conversion error:', error); |
|
|
addLogEntry(`Error: ${error.message}`, 'error'); |
|
|
statusMessage.textContent = `Error: ${error.message}`; |
|
|
convertSpinner.classList.add('hidden'); |
|
|
convertBtn.disabled = false; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function addLogEntry(message, type = 'info') { |
|
|
const logEntry = document.createElement('div'); |
|
|
logEntry.className = `log-entry log-${type}`; |
|
|
logEntry.textContent = message; |
|
|
conversionLog.appendChild(logEntry); |
|
|
conversionLog.scrollTop = conversionLog.scrollHeight; |
|
|
} |
|
|
|
|
|
|
|
|
function showError(message) { |
|
|
const errorDiv = document.createElement('div'); |
|
|
errorDiv.className = 'bg-red-50 border-l-4 border-red-500 p-4 mb-4'; |
|
|
errorDiv.innerHTML = ` |
|
|
<div class="flex"> |
|
|
<div class="flex-shrink-0"> |
|
|
<i class="fas fa-exclamation-circle text-red-500"></i> |
|
|
</div> |
|
|
<div class="ml-3"> |
|
|
<p class="text-sm text-red-700">${message}</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
dropzone.parentNode.insertBefore(errorDiv, dropzone.nextSibling); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
errorDiv.remove(); |
|
|
}, 5000); |
|
|
} |
|
|
|
|
|
function updateProgress(percent) { |
|
|
progressBar.style.width = percent + '%'; |
|
|
progressPercent.textContent = Math.round(percent) + '%'; |
|
|
} |
|
|
|
|
|
function conversionComplete() { |
|
|
|
|
|
convertSpinner.classList.add('hidden'); |
|
|
|
|
|
|
|
|
downloadSection.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
statusMessage.textContent = "Conversion completed successfully!"; |
|
|
addLogEntry('Conversion process finished successfully', 'success'); |
|
|
} |
|
|
|
|
|
|
|
|
downloadBtn.addEventListener('click', function() { |
|
|
if (!convertedBlob) return; |
|
|
|
|
|
const url = URL.createObjectURL(convertedBlob); |
|
|
const a = document.createElement('a'); |
|
|
a.href = url; |
|
|
|
|
|
const outputFormat = document.getElementById('outputFormat').value; |
|
|
const originalName = selectedFile.name.replace(/\.[^/.]+$/, ""); |
|
|
|
|
|
if (outputFormat === 'm3u8') { |
|
|
a.download = `${originalName}.m3u8`; |
|
|
} else { |
|
|
a.download = `${originalName}.ts`; |
|
|
} |
|
|
|
|
|
document.body.appendChild(a); |
|
|
a.click(); |
|
|
document.body.removeChild(a); |
|
|
URL.revokeObjectURL(url); |
|
|
}); |
|
|
|
|
|
|
|
|
function resetConverter() { |
|
|
selectedFile = null; |
|
|
convertedBlob = null; |
|
|
videoMetadata = {}; |
|
|
|
|
|
|
|
|
fileInfo.classList.add('hidden'); |
|
|
videoPreviewContainer.classList.add('hidden'); |
|
|
progressContainer.classList.add('hidden'); |
|
|
conversionLogContainer.classList.add('hidden'); |
|
|
downloadSection.classList.add('hidden'); |
|
|
convertBtn.disabled = true; |
|
|
convertSpinner.classList.add('hidden'); |
|
|
progressBar.style.width = '0%'; |
|
|
progressPercent.textContent = '0%'; |
|
|
statusMessage.textContent = ''; |
|
|
|
|
|
|
|
|
dropzone.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
fileInput.value = ''; |
|
|
|
|
|
|
|
|
if (videoPreview.src) { |
|
|
URL.revokeObjectURL(videoPreview.src); |
|
|
videoPreview.src = ''; |
|
|
} |
|
|
|
|
|
|
|
|
fileDuration.textContent = ''; |
|
|
fileResolution.textContent = ''; |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |