UNPKG

guessit-js

Version:

GuessIt JS (WASM) - Extract metadata from video filenames with WebAssembly performance

688 lines (593 loc) 25.8 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GuessIt JS - WASM Performance Demo</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; } .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; } .header h1 { font-size: 2.5em; margin-bottom: 10px; } .content { padding: 30px; } .demo-section { margin-bottom: 30px; padding: 20px; border: 2px solid #f0f0f0; border-radius: 10px; } .demo-section h3 { color: #2c3e50; margin-bottom: 15px; display: flex; align-items: center; gap: 10px; } .input-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; font-weight: 600; color: #34495e; } input[type="text"], input[type="number"] { width: 100%; padding: 15px; border: 2px solid #ddd; border-radius: 8px; font-size: 16px; transition: all 0.3s ease; } input:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } .button-group { display: flex; gap: 10px; flex-wrap: wrap; margin: 20px 0; } button { padding: 12px 24px; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; min-width: 140px; } .btn-primary { background: #667eea; color: white; } .btn-primary:hover { background: #5a6fd8; transform: translateY(-2px); } .btn-success { background: #28a745; color: white; } .btn-success:hover { background: #218838; } .btn-warning { background: #ffc107; color: #212529; } .btn-warning:hover { background: #e0a800; } .btn-secondary { background: #6c757d; color: white; } .performance-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0; } .performance-card { background: #f8f9fa; padding: 20px; border-radius: 10px; border-left: 5px solid #667eea; } .performance-card h4 { color: #2c3e50; margin-bottom: 15px; display: flex; align-items: center; gap: 10px; } .metric { display: flex; justify-content: space-between; margin: 10px 0; padding: 8px 0; border-bottom: 1px solid #e9ecef; } .metric:last-child { border-bottom: none; } .metric-value { font-weight: bold; color: #667eea; } .winner { border-left-color: #28a745; background: #f8fff9; } .winner h4 { color: #28a745; } .comparison { text-align: center; margin: 20px 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; } .comparison-value { font-size: 3em; font-weight: bold; margin: 10px 0; } .progress-bar { width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; } .progress-fill { height: 100%; background: linear-gradient(90deg, #28a745 0%, #20c997 100%); width: 0%; transition: width 0.3s ease; } .results { background: #2c3e50; color: #ecf0f1; padding: 20px; border-radius: 10px; font-family: 'Monaco', 'Menlo', monospace; font-size: 14px; line-height: 1.5; overflow-x: auto; min-height: 100px; white-space: pre-wrap; margin: 20px 0; } .test-files { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 10px; margin: 20px 0; } .test-file { background: white; padding: 12px; border: 2px solid #e9ecef; border-radius: 6px; cursor: pointer; transition: all 0.3s ease; font-family: monospace; font-size: 14px; } .test-file:hover { border-color: #667eea; background: #f8f9ff; } .test-file.selected { border-color: #28a745; background: #f8fff9; } .status { padding: 10px; border-radius: 5px; margin: 10px 0; font-weight: bold; } .status.loading { background: #fff3cd; color: #856404; } .status.success { background: #d4edda; color: #155724; } .status.error { background: #f8d7da; color: #721c24; } @media (max-width: 768px) { .performance-grid { grid-template-columns: 1fr; } .button-group { flex-direction: column; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>⚡ GuessIt JS - WASM Performance Demo</h1> <p>Compare JavaScript vs WebAssembly performance for video filename parsing</p> </div> <div class="content"> <!-- Single Parse Demo --> <div class="demo-section"> <h3>🎬 Single File Parsing</h3> <div class="input-group"> <label for="filename">Video Filename:</label> <input type="text" id="filename" placeholder="Enter a video filename to parse..." value="The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv"> </div> <div class="button-group"> <button class="btn-primary" onclick="parseSingle('js')">🔧 Parse with JavaScript</button> <button class="btn-success" onclick="parseSingle('wasm')">⚡ Parse with WASM</button> <button class="btn-warning" onclick="parseBoth()">⚖️ Compare Both</button> </div> <div class="results" id="singleResults">Click a button to see parsing results and performance...</div> </div> <!-- Benchmark Demo --> <div class="demo-section"> <h3>📊 Performance Benchmark</h3> <div class="input-group"> <label for="iterations">Number of Iterations:</label> <input type="number" id="iterations" value="1000" min="10" max="10000"> </div> <div class="test-files"> <div class="test-file selected" onclick="selectTestFile(this)" data-file="The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv"> The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv </div> <div class="test-file" onclick="selectTestFile(this)" data-file="Game.of.Thrones.S01E01.Winter.Is.Coming.HDTV.x264-LOL.mkv"> Game.of.Thrones.S01E01.Winter.Is.Coming.HDTV.x264-LOL.mkv </div> <div class="test-file" onclick="selectTestFile(this)" data-file="Breaking.Bad.S03E07.One.Minute.720p.HDTV.XviD-FQM.avi"> Breaking.Bad.S03E07.One.Minute.720p.HDTV.XviD-FQM.avi </div> <div class="test-file" onclick="selectTestFile(this)" data-file="Avengers.Endgame.2019.2160p.UHD.BluRay.x265.10bit.HDR.mkv"> Avengers.Endgame.2019.2160p.UHD.BluRay.x265.10bit.HDR.mkv </div> </div> <div class="button-group"> <button class="btn-primary" onclick="runBenchmark()">🚀 Run Benchmark</button> <button class="btn-secondary" onclick="clearBenchmark()">🗑️ Clear Results</button> </div> <div id="benchmarkStatus"></div> <div id="benchmarkProgress" style="display: none;"> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> </div> <div class="performance-grid" id="performanceGrid" style="display: none;"> <div class="performance-card" id="jsCard"> <h4>🔧 JavaScript Engine</h4> <div class="metric"> <span>Total Time:</span> <span class="metric-value" id="jsTotalTime">-</span> </div> <div class="metric"> <span>Average Time:</span> <span class="metric-value" id="jsAvgTime">-</span> </div> <div class="metric"> <span>Operations/sec:</span> <span class="metric-value" id="jsOpsPerSec">-</span> </div> </div> <div class="performance-card" id="wasmCard"> <h4>⚡ WebAssembly Engine</h4> <div class="metric"> <span>Total Time:</span> <span class="metric-value" id="wasmTotalTime">-</span> </div> <div class="metric"> <span>Average Time:</span> <span class="metric-value" id="wasmAvgTime">-</span> </div> <div class="metric"> <span>Operations/sec:</span> <span class="metric-value" id="wasmOpsPerSec">-</span> </div> </div> </div> <div class="comparison" id="comparison" style="display: none;"> <div>🏆 WASM Performance Improvement</div> <div class="comparison-value" id="speedup">-</div> <div>times faster than JavaScript</div> </div> <div class="results" id="benchmarkResults" style="display: none;"></div> </div> <!-- Memory Usage Demo --> <div class="demo-section"> <h3>🧠 Memory Usage Comparison</h3> <p>This demo shows the memory efficiency of different engines:</p> <div class="performance-grid"> <div class="performance-card"> <h4>📦 Bundle Sizes</h4> <div class="metric"> <span>JavaScript Engine:</span> <span class="metric-value">~18KB</span> </div> <div class="metric"> <span>WASM Binary:</span> <span class="metric-value">~38KB</span> </div> <div class="metric"> <span>WASM Loader:</span> <span class="metric-value">~11KB</span> </div> <div class="metric"> <span>Total WASM:</span> <span class="metric-value">~49KB</span> </div> </div> <div class="performance-card"> <h4>💾 Runtime Memory</h4> <div class="metric"> <span>JS Heap Usage:</span> <span class="metric-value">~2-5MB</span> </div> <div class="metric"> <span>WASM Memory:</span> <span class="metric-value">~64KB</span> </div> <div class="metric"> <span>Total WASM:</span> <span class="metric-value">~200KB</span> </div> <div class="metric"> <span>Python GuessIt:</span> <span class="metric-value">~10-20MB</span> </div> </div> </div> </div> </div> </div> <script type="module"> // Import GuessIt functions import { guessit } from '../src/index.js'; import { guessitWasm, initWasm } from '../src/wasm/wasm-loader.js'; let wasmInitialized = false; let selectedTestFile = 'The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv'; // Initialize WASM on page load async function initializeWasm() { try { await initWasm(); wasmInitialized = true; console.log('WASM initialized successfully'); } catch (error) { console.warn('WASM initialization failed, falling back to simulation:', error); wasmInitialized = false; } } // Initialize on page load initializeWasm(); window.selectTestFile = function(element) { document.querySelectorAll('.test-file').forEach(el => el.classList.remove('selected')); element.classList.add('selected'); selectedTestFile = element.dataset.file; }; window.parseSingle = async function(engine) { const filename = document.getElementById('filename').value.trim(); const resultsEl = document.getElementById('singleResults'); if (!filename) { resultsEl.textContent = 'Please enter a filename'; return; } try { let result, parseTime; const startTime = performance.now(); if (engine === 'wasm') { if (!wasmInitialized) { await initializeWasm(); } result = await guessitWasm(filename); } else { result = guessit(filename); } const endTime = performance.now(); parseTime = (endTime - startTime).toFixed(4); const output = { filename: filename, engine: engine === 'wasm' ? 'WebAssembly' : 'JavaScript', parse_time_ms: parseTime, properties_detected: Object.keys(result).length, result: result }; resultsEl.textContent = JSON.stringify(output, null, 2); } catch (error) { resultsEl.textContent = `Error: ${error.message}`; } }; window.parseBoth = async function() { const filename = document.getElementById('filename').value.trim(); const resultsEl = document.getElementById('singleResults'); if (!filename) { resultsEl.textContent = 'Please enter a filename'; return; } try { // JavaScript parsing const jsStartTime = performance.now(); const jsResult = guessit(filename); const jsEndTime = performance.now(); const jsParseTime = (jsEndTime - jsStartTime).toFixed(4); // WASM parsing if (!wasmInitialized) { await initializeWasm(); } const wasmStartTime = performance.now(); const wasmResult = await guessitWasm(filename); const wasmEndTime = performance.now(); const wasmParseTime = (wasmEndTime - wasmStartTime).toFixed(4); const speedup = (jsParseTime / wasmParseTime).toFixed(2); const comparison = { filename: filename, javascript: { parse_time_ms: jsParseTime, properties_detected: Object.keys(jsResult).length, result: jsResult }, webassembly: { parse_time_ms: wasmParseTime, properties_detected: Object.keys(wasmResult).length, result: wasmResult }, performance: { speedup: `${speedup}x faster`, time_saved_ms: (parseFloat(jsParseTime) - parseFloat(wasmParseTime)).toFixed(4) } }; resultsEl.textContent = JSON.stringify(comparison, null, 2); } catch (error) { resultsEl.textContent = `Error: ${error.message}`; } }; window.runBenchmark = async function() { const iterations = parseInt(document.getElementById('iterations').value); const statusEl = document.getElementById('benchmarkStatus'); const progressEl = document.getElementById('benchmarkProgress'); const progressFillEl = document.getElementById('progressFill'); const performanceGridEl = document.getElementById('performanceGrid'); const comparisonEl = document.getElementById('comparison'); const resultsEl = document.getElementById('benchmarkResults'); statusEl.innerHTML = '<div class="status loading">🚀 Running benchmark...</div>'; progressEl.style.display = 'block'; performanceGridEl.style.display = 'none'; comparisonEl.style.display = 'none'; resultsEl.style.display = 'none'; try { // JavaScript benchmark statusEl.innerHTML = '<div class="status loading">🔧 Benchmarking JavaScript engine...</div>'; progressFillEl.style.width = '25%'; const jsStartTime = performance.now(); for (let i = 0; i < iterations; i++) { guessit(selectedTestFile); if (i % 100 === 0) { progressFillEl.style.width = `${25 + (i / iterations) * 25}%`; await new Promise(resolve => setTimeout(resolve, 1)); } } const jsEndTime = performance.now(); const jsTotalTime = jsEndTime - jsStartTime; const jsAvgTime = jsTotalTime / iterations; const jsOpsPerSec = 1000 / jsAvgTime; progressFillEl.style.width = '50%'; // WASM benchmark statusEl.innerHTML = '<div class="status loading">⚡ Benchmarking WebAssembly engine...</div>'; if (!wasmInitialized) { await initializeWasm(); } const wasmStartTime = performance.now(); for (let i = 0; i < iterations; i++) { await guessitWasm(selectedTestFile); if (i % 100 === 0) { progressFillEl.style.width = `${50 + (i / iterations) * 25}%`; await new Promise(resolve => setTimeout(resolve, 1)); } } const wasmEndTime = performance.now(); const wasmTotalTime = wasmEndTime - wasmStartTime; const wasmAvgTime = wasmTotalTime / iterations; const wasmOpsPerSec = 1000 / wasmAvgTime; progressFillEl.style.width = '100%'; // Update UI document.getElementById('jsTotalTime').textContent = `${jsTotalTime.toFixed(2)}ms`; document.getElementById('jsAvgTime').textContent = `${jsAvgTime.toFixed(4)}ms`; document.getElementById('jsOpsPerSec').textContent = Math.round(jsOpsPerSec).toLocaleString(); document.getElementById('wasmTotalTime').textContent = `${wasmTotalTime.toFixed(2)}ms`; document.getElementById('wasmAvgTime').textContent = `${wasmAvgTime.toFixed(4)}ms`; document.getElementById('wasmOpsPerSec').textContent = Math.round(wasmOpsPerSec).toLocaleString(); const speedup = (jsOpsPerSec / wasmOpsPerSec).toFixed(1); document.getElementById('speedup').textContent = speedup; // Highlight winner const jsCard = document.getElementById('jsCard'); const wasmCard = document.getElementById('wasmCard'); jsCard.classList.remove('winner'); wasmCard.classList.remove('winner'); if (wasmOpsPerSec > jsOpsPerSec) { wasmCard.classList.add('winner'); } else { jsCard.classList.add('winner'); } const benchmarkData = { test_file: selectedTestFile, iterations: iterations, javascript: { total_time_ms: jsTotalTime.toFixed(2), average_time_ms: jsAvgTime.toFixed(4), operations_per_second: Math.round(jsOpsPerSec) }, webassembly: { total_time_ms: wasmTotalTime.toFixed(2), average_time_ms: wasmAvgTime.toFixed(4), operations_per_second: Math.round(wasmOpsPerSec) }, performance: { speedup: `${speedup}x`, winner: wasmOpsPerSec > jsOpsPerSec ? 'WebAssembly' : 'JavaScript' } }; statusEl.innerHTML = '<div class="status success">✅ Benchmark completed successfully!</div>'; progressEl.style.display = 'none'; performanceGridEl.style.display = 'grid'; comparisonEl.style.display = 'block'; resultsEl.style.display = 'block'; resultsEl.textContent = JSON.stringify(benchmarkData, null, 2); } catch (error) { statusEl.innerHTML = `<div class="status error">❌ Benchmark failed: ${error.message}</div>`; progressEl.style.display = 'none'; } }; window.clearBenchmark = function() { document.getElementById('benchmarkStatus').innerHTML = ''; document.getElementById('benchmarkProgress').style.display = 'none'; document.getElementById('performanceGrid').style.display = 'none'; document.getElementById('comparison').style.display = 'none'; document.getElementById('benchmarkResults').style.display = 'none'; }; </script> </body> </html>