Instrument gratuit - Nu este necesar cont

Eliminator gratuit de duplicate M3U și IPTV

Curățați lista M3U, M3U8 sau IPTV online eliminând canalele duplicate în câteva secunde. Potrivire după nume, URL sau ambele. Descărcați imediat un fișier curat.

Trage și plasează fișierul M3U aici

sau

.m3u și .m3u8 acceptate - Procesate local în browserul tău - Nu se încarcă niciodată pe serverele noastre

100% privat

Fișierul tău nu părăsește niciodată browserul. Toată procesarea rulează local - nimic nu este trimis la serverele noastre.

Potrivire flexibilă

Potriviți duplicatele după URL-ul streamului, numele canalului sau ambele simultan. Păstrează prima apariție.

Descărcare instantanee

Descărcați imediat fișierul M3U curățat. Fără așteptare, fără email, fără înregistrare.

Vrei să faci mai mult cu playlist-ul tău?

Creează un cont gratuit M3U Maker pentru a filtra canale, a crea playlist-uri personalizate și a le menține sincronizate automat cu fișierul sursă.

(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); } })();