Alat Gratis - Tidak perlu akun

Validator playlist M3U & IPTV gratis

Periksa dan validasi playlist M3U, M3U8 atau IPTV Anda secara online. Mendeteksi error, header yang hilang, URL tidak valid dan saluran duplikat secara instan.

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.

Hasil Instan

Validasi selesai dalam milidetik, bahkan untuk playlist besar dengan puluhan ribu saluran.

Laporan terperinci

Memeriksa header #EXTM3U yang hilang, URL tidak valid, atribut group-title yang hilang, dan saluran duplikat.

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'); 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 = ''; }); var loading = document.getElementById('loading'); function processFile(file) { zone.style.display = 'none'; loading.style.display = ''; var reader = new FileReader(); reader.onload = function(e) { validateM3U(e.target.result, file.name); }; reader.readAsText(file, 'UTF-8'); } function validateM3U(content, filename) { var lines = content.split(/\r?\n/); var errors = [], warnings = []; var channelCount = 0; var urls = {}, names = {}; var i = 0; if (lines[0] && lines[0].trim().startsWith('#EXTM3U')) { i = 1; } else { errors.push({ line: 1, msg: 'Missing #EXTM3U header on line 1' }); } while (i < lines.length) { var line = lines[i].trim(); if (!line || line === '#EXTM3U') { i++; continue; } if (line.startsWith('#EXTINF:')) { channelCount++; var lineNum = i + 1; var nameMatch = line.match(/,(.+)$/); var name = nameMatch ? nameMatch[1].trim() : ''; if (!name) { warnings.push({ line: lineNum, msg: 'Channel #' + channelCount + ': missing name after comma in #EXTINF' }); } if (line.indexOf('group-title') === -1) { warnings.push({ line: lineNum, msg: 'Channel #' + channelCount + (name ? ' "' + name + '"' : '') + ': missing group-title attribute' }); } if (name) { var lname = name.toLowerCase(); if (names[lname] !== undefined) { warnings.push({ line: lineNum, msg: 'Duplicate channel name: "' + name + '" (also on line ' + names[lname] + ')' }); } else { names[lname] = lineNum; } } i++; while (i < lines.length && !lines[i].trim()) i++; var urlLine = lines[i] ? lines[i].trim() : ''; var urlLineNum = i + 1; if (!urlLine || urlLine.startsWith('#')) { errors.push({ line: lineNum, msg: 'Channel #' + channelCount + (name ? ' "' + name + '"' : '') + ': no stream URL found after #EXTINF' }); } else { if (!/^(https?|rtsp|rtp|udp|igmp|mms|rtmp):\/\//i.test(urlLine)) { warnings.push({ line: urlLineNum, msg: 'Channel #' + channelCount + (name ? ' "' + name + '"' : '') + ': unusual URL format' }); } if (urls[urlLine] !== undefined) { warnings.push({ line: urlLineNum, msg: 'Duplicate stream URL for "' + name + '" (also on line ' + urls[urlLine] + ')' }); } else { urls[urlLine] = urlLineNum; } } } i++; } showResults(filename, channelCount, errors, warnings); } function showResults(filename, channelCount, errors, warnings) { document.getElementById('resultsFilename').textContent = filename; var statsRow = document.getElementById('statsRow'); var dupeCount = warnings.filter(function(w) { return w.msg.indexOf('Duplicate') === 0; }).length; statsRow.textContent = ''; [ { val: channelCount.toLocaleString(), label: 'Channels', cls: '' }, { val: errors.length, label: 'Errors', cls: errors.length > 0 ? 'tool-stat--error' : '' }, { val: warnings.length, label: 'Warnings', cls: warnings.length > 0 ? 'tool-stat--warning' : '' }, { val: dupeCount, label: 'Duplicates', 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); }); var total = errors.length * 3 + warnings.length; var score = Math.max(0, Math.min(100, Math.round(100 - (total / Math.max(channelCount, 1)) * 200))); var scoreColor = score >= 80 ? 'var(--accent-success)' : score >= 50 ? 'var(--accent-warning)' : 'var(--accent-danger)'; document.getElementById('scoreText').textContent = score + '/100 - ' + (score >= 80 ? 'Good' : score >= 50 ? 'Fair' : 'Poor'); var fill = document.getElementById('scoreFill'); fill.style.width = '0%'; setTimeout(function() { fill.style.width = score + '%'; fill.style.background = scoreColor; }, 50); var issueList = document.getElementById('issueList'); issueList.textContent = ''; function renderGroup(items, type) { if (!items.length) return; var group = document.createElement('div'); group.className = 'tool-issue-group'; var header = document.createElement('div'); header.className = 'tool-issue-group-header tool-issue--' + type; header.textContent = (type === 'error' ? 'Errors' : 'Warnings') + ' (' + items.length + ')'; group.appendChild(header); var list = document.createElement('div'); list.className = 'tool-issue-list'; items.slice(0, 50).forEach(function(issue) { var item = document.createElement('div'); item.className = 'tool-issue-item'; var lineSpan = document.createElement('span'); lineSpan.className = 'tool-issue-line'; lineSpan.textContent = 'L' + issue.line; var msgSpan = document.createElement('span'); msgSpan.className = 'tool-issue-msg'; msgSpan.textContent = issue.msg; item.appendChild(lineSpan); item.appendChild(msgSpan); list.appendChild(item); }); if (items.length > 50) { var more = document.createElement('div'); more.className = 'tool-issue-more'; more.textContent = '… and ' + (items.length - 50) + ' more ' + type + 's'; list.appendChild(more); } group.appendChild(list); issueList.appendChild(group); } renderGroup(errors, 'error'); renderGroup(warnings, 'warning'); document.getElementById('successMsg').style.display = (errors.length + warnings.length === 0) ? '' : 'none'; loading.style.display = 'none'; results.style.display = ''; } })();