Alat Gratis - Tidak perlu akun

Penghapus duplikat M3U & IPTV gratis

Bersihkan playlist M3U, M3U8 atau IPTV Anda secara online dengan menghapus saluran duplikat dalam hitungan detik. Cocokkan berdasarkan nama, URL, atau keduanya. Unduh file bersih seketika.

Seret & lepas file M3U Anda di sini

atau

.m3u dan .m3u8 didukung - Diproses secara lokal di browser Anda - Tidak pernah diunggah ke server kami

100% Privat

File Anda tidak pernah meninggalkan browser Anda. Semua pemrosesan berjalan secara lokal - tidak ada yang dikirim ke server kami.

Pencocokan fleksibel

Cocokkan duplikat berdasarkan URL stream, nama saluran, atau keduanya sekaligus. Menyimpan kemunculan pertama.

Unduhan instan

Unduh file M3U yang sudah dibersihkan segera. Tanpa menunggu, tanpa email, tanpa pendaftaran.

Ingin melakukan lebih banyak dengan playlist Anda?

Buat akun M3U Maker gratis untuk memfilter saluran, membuat playlist kustom, dan menjaganya tetap tersinkronisasi otomatis dengan file sumber.

(function() { var zone = document.getElementById('uploadZone'); var fileInput = document.getElementById('fileInput'); var results = document.getElementById('results'); var resetBtn = document.getElementById('resetBtn'); var browseBtn = document.getElementById('browseBtn'); var reprocessBtn = document.getElementById('reprocessBtn'); var downloadBtn = document.getElementById('downloadBtn'); var matchUrl = document.getElementById('matchUrl'); var matchName = document.getElementById('matchName'); var parsedChannels = []; var originalHeader = '#EXTM3U'; var currentFilename = ''; browseBtn.addEventListener('click', function(e) { e.stopPropagation(); fileInput.click(); }); zone.addEventListener('click', function() { fileInput.click(); }); zone.addEventListener('dragover', function(e) { e.preventDefault(); zone.classList.add('drag-over'); }); zone.addEventListener('dragleave', function() { zone.classList.remove('drag-over'); }); zone.addEventListener('drop', function(e) { e.preventDefault(); zone.classList.remove('drag-over'); if (e.dataTransfer.files[0]) processFile(e.dataTransfer.files[0]); }); fileInput.addEventListener('change', function() { if (fileInput.files[0]) processFile(fileInput.files[0]); }); resetBtn.addEventListener('click', function() { results.style.display = 'none'; loading.style.display = 'none'; zone.style.display = ''; fileInput.value = ''; parsedChannels = []; }); reprocessBtn.addEventListener('click', function() { showResults(); }); downloadBtn.addEventListener('click', function() { downloadClean(); }); var loading = document.getElementById('loading'); function processFile(file) { currentFilename = file.name; zone.style.display = 'none'; loading.style.display = ''; var reader = new FileReader(); reader.onload = function(e) { parseM3U(e.target.result); }; reader.readAsText(file, 'UTF-8'); } function parseM3U(content) { var lines = content.split(/\r?\n/); parsedChannels = []; originalHeader = '#EXTM3U'; var i = 0; if (lines[0] && lines[0].trim().startsWith('#EXTM3U')) { originalHeader = lines[0].trim(); i = 1; } while (i < lines.length) { var line = lines[i].trim(); if (!line) { i++; continue; } if (line.startsWith('#EXTINF:')) { var extinf = line; var nameMatch = line.match(/,(.+)$/); var name = nameMatch ? nameMatch[1].trim() : ''; i++; while (i < lines.length && !lines[i].trim()) i++; var url = lines[i] ? lines[i].trim() : ''; if (url && !url.startsWith('#')) { parsedChannels.push({ extinf: extinf, name: name, url: url }); } } i++; } loading.style.display = 'none'; results.style.display = ''; document.getElementById('resultsFilename').textContent = currentFilename; showResults(); } function showResults() { var byUrl = matchUrl.checked; var byName = matchName.checked; if (!byUrl && !byName) { matchUrl.checked = true; byUrl = true; } var seenUrls = {}, seenNames = {}; var kept = [], dupes = []; parsedChannels.forEach(function(ch) { var isDupe = false; var dupeType = ''; if (byUrl && ch.url && seenUrls[ch.url]) { isDupe = true; dupeType = 'url'; } if (byName && ch.name && seenNames[ch.name.toLowerCase()]) { isDupe = true; dupeType = dupeType ? dupeType + '+name' : 'name'; } if (isDupe) { dupes.push({ ch: ch, type: dupeType }); } else { kept.push(ch); if (byUrl && ch.url) seenUrls[ch.url] = true; if (byName && ch.name) seenNames[ch.name.toLowerCase()] = true; } }); // Stats var statsRow = document.getElementById('statsRow'); statsRow.textContent = ''; [ { val: parsedChannels.length.toLocaleString(), label: 'Total channels', cls: '' }, { val: dupes.length, label: 'Duplicates found', cls: dupes.length > 0 ? 'tool-stat--warning' : '' }, { val: kept.length.toLocaleString(), label: 'Unique channels', cls: kept.length > 0 ? 'tool-stat--success' : '' }, { val: dupes.length > 0 ? Math.round(dupes.length / parsedChannels.length * 100) + '%' : '0%', label: 'Reduction', cls: '' } ].forEach(function(s) { var div = document.createElement('div'); div.className = 'tool-stat ' + s.cls; var val = document.createElement('span'); val.className = 'tool-stat-value'; val.textContent = s.val; var lbl = document.createElement('span'); lbl.className = 'tool-stat-label'; lbl.textContent = s.label; div.appendChild(val); div.appendChild(lbl); statsRow.appendChild(div); }); // Dupes list var dupeSection = document.getElementById('dupeSection'); var nodupeMsg = document.getElementById('nodupeMsg'); var downloadBar = document.getElementById('downloadBar'); if (dupes.length > 0) { document.getElementById('dupeGroupHeader').textContent = 'Duplicate channels to be removed (' + dupes.length + ')'; var dupeList = document.getElementById('dupeList'); dupeList.textContent = ''; dupes.slice(0, 100).forEach(function(d) { var item = document.createElement('div'); item.className = 'tool-dupe-item'; var badge = document.createElement('span'); badge.className = 'tool-dupe-badge tool-dupe-badge--' + (d.type === 'url' ? 'url' : 'name'); badge.textContent = d.type === 'url' ? 'URL' : d.type === 'name' ? 'Name' : 'URL+Name'; item.appendChild(badge); var info = document.createElement('div'); info.className = 'tool-dupe-info'; var nameEl = document.createElement('div'); nameEl.className = 'tool-dupe-name'; nameEl.textContent = d.ch.name || '(no name)'; var urlEl = document.createElement('div'); urlEl.className = 'tool-dupe-url'; urlEl.textContent = d.ch.url; info.appendChild(nameEl); info.appendChild(urlEl); item.appendChild(info); dupeList.appendChild(item); }); if (dupes.length > 100) { var more = document.createElement('div'); more.className = 'tool-issue-more'; more.textContent = '… and ' + (dupes.length - 100) + ' more duplicates'; dupeList.appendChild(more); } dupeSection.style.display = ''; nodupeMsg.style.display = 'none'; document.getElementById('downloadInfo').textContent = kept.length.toLocaleString() + ' channels, ' + dupes.length + ' duplicates removed'; downloadBar.style.display = ''; downloadBtn._kept = kept; } else { dupeSection.style.display = 'none'; nodupeMsg.style.display = ''; downloadBar.style.display = 'none'; } } function downloadClean() { var kept = downloadBtn._kept; if (!kept) return; var lines = [originalHeader]; kept.forEach(function(ch) { lines.push(ch.extinf); lines.push(ch.url); lines.push(''); }); var blob = new Blob([lines.join('\n')], { type: 'audio/x-mpegurl' }); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); var base = currentFilename.replace(/\.[^.]+$/, ''); a.download = base + '-cleaned.m3u'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); } })();