Bulk Video Compressor (Browser)
Select multiple videos and compress them in your browser using ffmpeg.wasm. Choose resolution & bitrate, process in batch, then download a zip of compressed files.
‘; return; }
files.forEach((it, idx)=>{
const el = document.createElement(‘div’); el.className=’file-item’;
const left = document.createElement(‘div’);
left.innerHTML = `
`;
const right = document.createElement(‘div’);
right.style.display=’flex’; right.style.gap=’8px’; right.style.alignItems=’center’;
const remove = document.createElement(‘button’); remove.className=’ghost’; remove.textContent=’Remove’; remove.onclick = ()=>{ files.splice(idx,1); renderFileList(); };
const st = document.createElement(‘div’); st.className=’small’; st.textContent = it.status;
right.appendChild(st); right.appendChild(remove);
el.appendChild(left); el.appendChild(right);
fileList.appendChild(el);
});
}
async function loadFFmpeg(){
if(ffmpeg) return ffmpeg;
status.textContent = ‘Loading FFmpeg (this may take a while)…’;
ffmpeg = createFFmpeg({ log:true });
ffmpeg.setLogger(({ type, message })=>{
// console.log(type, message);
});
ffmpeg.setProgress(({ ratio })=>{
// ratio is 0..1 for single ffmpeg job; we will map it per-file in process loop
});
await ffmpeg.load();
status.textContent = ‘FFmpeg loaded’;
return ffmpeg;
}
startBtn.addEventListener(‘click’, async ()=>{
if(files.length===0){ alert(‘Please add some video files first’); return; }
startBtn.disabled = true; startBtn.textContent=’Processing…’;
try{
await loadFFmpeg();
}catch(err){ console.error(err); alert(‘Failed to load FFmpeg. Try a modern desktop browser.’); startBtn.disabled=false; startBtn.textContent=’Start Compressing’; return; }
zip = new JSZip();
let completed = 0;
for(let i=0;i
const fileRatio = Math.round(ratio*100);
const overallRatio = ((completed + ratio) / files.length) * 100;
overallBar.style.width = overallRatio + ‘%’;
overallText.textContent = `${i+1} / ${files.length} ${fileRatio}%`;
});
await ffmpeg.run(…args);
const outData = ffmpeg.FS(‘readFile’, outName);
const blob = new Blob([outData.buffer], { type: ‘video/mp4’ });
item.compressedBlob = blob;
item.status = ‘done’;
renderFileList();
// add to zip
zip.file(item.outName, blob);
// cleanup FS
try{ ffmpeg.FS(‘unlink’, inName); ffmpeg.FS(‘unlink’, outName); }catch(e){}
}catch(err){ console.error(err); item.status=’error’; renderFileList(); }
completed++;
overallBar.style.width = ((completed/files.length)*100) + ‘%’;
overallText.textContent = `${completed} / ${files.length}`;
}
// create zip and offer download
status.textContent = ‘Generating zip…’;
const content = await zip.generateAsync({ type: ‘blob’ }, (meta)=>{
overallBar.style.width = (meta.percent/100) * 100 + ‘%’;
overallText.textContent = `Zipping ${Math.round(meta.percent)}%`;
});
const url = URL.createObjectURL(content);
const a = document.createElement(‘a’); a.href = url; a.download = ‘compressed_videos.zip’; document.body.appendChild(a); a.click(); a.remove();
status.textContent = ‘Done downloaded compressed_videos.zip’;
startBtn.disabled=false; startBtn.textContent=’Start Compressing’;
});
function sanitizeName(name){ return name.replace(/[^a-z0-9_.-]/gi,’_’); }
function changeExtension(name,ext){ return name.replace(/\.[^/.]+$/, ”) + ‘.’ + ext; }



Leave a Reply