Ferramenta gratuita - Sem conta necessária

Analisador gratuito de listas M3U e IPTV

Analise a sua lista M3U, M3U8 ou IPTV online. Visão completa: contagem de canais por grupo, estatísticas de qualidade (4K/HD/SD), distribuição de protocolos e pontuação de saúde de metadados.

Arraste e largue o seu ficheiro M3U aqui

ou

.m3u e .m3u8 suportados - Processado localmente no seu browser - Nunca enviado para os nossos servidores

100% privado

O seu ficheiro nunca abandona o seu browser. Todo o processamento é local.

Instantâneo e detalhado

Distribuição de qualidade, estatísticas de protocolo e classificação de grupos - em segundos.

Suporte para ficheiros grandes

Gere playlists pequenas e muito grandes - testado com ficheiros de 50 000+ canais.

Quer fazer mais com a sua playlist?

Crie uma conta gratuita M3U Maker para filtrar canais, criar playlists personalizadas e mantê-las sincronizadas.

(function() { var zone = document.getElementById('uploadZone'); var fileInput = document.getElementById('fileInput'); var loading = document.getElementById('loading'); var results = document.getElementById('results'); var resetBtn = document.getElementById('resetBtn'); var browseBtn = document.getElementById('browseBtn'); 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 = ''; }); function processFile(file) { zone.style.display = 'none'; loading.style.display = ''; document.getElementById('resultsFilename').textContent = file.name; var reader = new FileReader(); reader.onload = function(e) { setTimeout(function() { analyzeM3U(e.target.result); }, 30); }; reader.readAsText(file, 'UTF-8'); } function analyzeM3U(content) { var lines = content.split(/\r?\n/); var channels = 0, groups = {}, noGroup = 0; var quality = { '4K': 0, 'FHD': 0, 'HD': 0, 'SD': 0 }; var protocols = {}; var hasLogo = 0, hasGroup = 0, hasTvgId = 0, hasName = 0; var i = lines[0] && lines[0].trim().startsWith('#EXTM3U') ? 1 : 0; while (i < lines.length) { var line = lines[i].trim(); if (!line) { i++; continue; } if (line.startsWith('#EXTINF:')) { channels++; // Name var nameMatch = line.match(/,(.+)$/); var name = nameMatch ? nameMatch[1].trim() : ''; if (name) hasName++; // Group var groupMatch = line.match(/group-title="([^"]*)"/i); var group = groupMatch ? groupMatch[1].trim() : ''; if (group) { hasGroup++; groups[group] = (groups[group] || 0) + 1; } else { noGroup++; } // Logo if (/tvg-logo="[^"]+"/i.test(line)) hasLogo++; // tvg-id if (/tvg-id="[^"]+"/i.test(line)) hasTvgId++; // Quality from name var uname = name.toUpperCase(); if (/\b(4K|UHD|2160P)\b/.test(uname)) quality['4K']++; else if (/\b(FHD|1080[PI]|FULLHD)\b/.test(uname)) quality['FHD']++; else if (/\b(HD|720[PI])\b/.test(uname)) quality['HD']++; else quality['SD']++; // URL on next line i++; while (i < lines.length && !lines[i].trim()) i++; var url = lines[i] ? lines[i].trim() : ''; if (url && !url.startsWith('#')) { var protoMatch = url.match(/^([a-z][a-z0-9+\-.]*):\/\//i); var proto = protoMatch ? protoMatch[1].toLowerCase() : 'other'; protocols[proto] = (protocols[proto] || 0) + 1; } } i++; } showResults(channels, groups, noGroup, quality, protocols, hasLogo, hasGroup, hasTvgId, hasName); } function pct(val, total) { return total > 0 ? Math.round(val / total * 100) : 0; } function showResults(channels, groups, noGroup, quality, protocols, hasLogo, hasGroup, hasTvgId, hasName) { var groupCount = Object.keys(groups).length + (noGroup > 0 ? 1 : 0); // Summary stats var statsEl = document.getElementById('summaryStats'); statsEl.textContent = ''; [ { val: channels.toLocaleString(), label: 'Total channels' }, { val: groupCount, label: 'Groups' }, { val: pct(quality['4K'] + quality['FHD'] + quality['HD'], channels) + '%', label: 'HD or better' }, { val: pct(hasLogo, channels) + '%', label: 'Has logo' }, { val: pct(hasGroup, channels) + '%', label: 'Has group' } ].forEach(function(s) { var div = document.createElement('div'); div.className = 'tool-stat'; 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); statsEl.appendChild(div); }); // Quality bars var qualityData = [ { label: '4K / UHD', count: quality['4K'], color: 'var(--accent-secondary)' }, { label: 'Full HD (1080p)', count: quality['FHD'], color: 'var(--accent-primary)' }, { label: 'HD (720p)', count: quality['HD'], color: '#00b894' }, { label: 'SD / Unknown', count: quality['SD'], color: 'var(--text-muted)' } ]; renderBars('qualityBars', qualityData, channels); // Protocol bars var protoData = Object.entries(protocols) .sort(function(a, b) { return b[1] - a[1]; }) .map(function(e) { return { label: e[0], count: e[1], color: 'var(--accent-primary)' }; }); renderBars('protocolBars', protoData, channels); // Metadata bars var metaData = [ { label: 'Has channel name', count: hasName, color: 'var(--accent-primary)' }, { label: 'Has group-title', count: hasGroup, color: 'var(--accent-primary)' }, { label: 'Has tvg-logo', count: hasLogo, color: 'var(--accent-primary)' }, { label: 'Has tvg-id', count: hasTvgId, color: 'var(--accent-primary)' } ]; renderBars('metaBars', metaData, channels); // Groups list var sortedGroups = Object.entries(groups).sort(function(a, b) { return b[1] - a[1]; }); if (noGroup > 0) sortedGroups.push(['(no group)', noGroup]); var maxCount = sortedGroups.length > 0 ? sortedGroups[0][1] : 1; document.getElementById('groupsCount').textContent = groupCount + ' groups'; var groupsList = document.getElementById('groupsList'); groupsList.textContent = ''; var showAll = false; var limit = 15; function renderGroups(all) { groupsList.textContent = ''; var toShow = all ? sortedGroups : sortedGroups.slice(0, limit); toShow.forEach(function(g) { var item = document.createElement('div'); item.className = 'analyzer-group-item'; var label = document.createElement('div'); label.className = 'analyzer-group-label'; var name = document.createElement('span'); name.className = 'analyzer-group-name'; name.textContent = g[0]; var count = document.createElement('span'); count.className = 'analyzer-group-count'; count.textContent = g[1].toLocaleString(); label.appendChild(name); label.appendChild(count); var barWrap = document.createElement('div'); barWrap.className = 'analyzer-group-bar-wrap'; var bar = document.createElement('div'); bar.className = 'analyzer-group-bar'; bar.style.width = '0%'; barWrap.appendChild(bar); setTimeout(function() { bar.style.width = Math.round(g[1] / maxCount * 100) + '%'; }, 50); item.appendChild(label); item.appendChild(barWrap); groupsList.appendChild(item); }); } renderGroups(false); var moreEl = document.getElementById('groupsMore'); if (sortedGroups.length > limit) { moreEl.style.display = ''; var remaining = sortedGroups.length - limit; var btn = document.createElement('button'); btn.className = 'btn btn-ghost btn-sm'; btn.textContent = 'Show all ' + sortedGroups.length + ' groups'; btn.addEventListener('click', function() { renderGroups(true); moreEl.style.display = 'none'; }); moreEl.textContent = ''; moreEl.appendChild(btn); } else { moreEl.style.display = 'none'; } loading.style.display = 'none'; results.style.display = ''; } function renderBars(containerId, data, total) { var container = document.getElementById(containerId); container.textContent = ''; var max = data.reduce(function(m, d) { return Math.max(m, d.count); }, 1); data.forEach(function(d) { if (d.count === 0) return; var item = document.createElement('div'); item.className = 'analyzer-bar-item'; var header = document.createElement('div'); header.className = 'analyzer-bar-header'; var lbl = document.createElement('span'); lbl.className = 'analyzer-bar-label'; lbl.textContent = d.label; var cnt = document.createElement('span'); cnt.className = 'analyzer-bar-count'; cnt.textContent = d.count.toLocaleString() + ' (' + pct(d.count, total) + '%)'; header.appendChild(lbl); header.appendChild(cnt); var barWrap = document.createElement('div'); barWrap.className = 'analyzer-bar-wrap'; var bar = document.createElement('div'); bar.className = 'analyzer-bar-fill'; bar.style.width = '0%'; bar.style.background = d.color; barWrap.appendChild(bar); setTimeout(function(b, w) { b.style.width = w + '%'; }.bind(null, bar, Math.round(d.count / max * 100)), 50); item.appendChild(header); item.appendChild(barWrap); container.appendChild(item); }); } })();