UNPKG

pdq-wasm

Version:

WebAssembly bindings for Meta's PDQ perceptual image hashing algorithm

156 lines (129 loc) 4.07 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>PDQ Web Worker Example</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; } .container { border: 2px dashed #ccc; border-radius: 8px; padding: 40px; text-align: center; background: #f9f9f9; } input[type="file"] { margin: 20px 0; } #results { margin-top: 30px; text-align: left; } .hash-result { background: white; padding: 15px; margin: 10px 0; border-radius: 4px; border-left: 4px solid #4CAF50; } .error { border-left-color: #f44336; } .hash { font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all; color: #333; margin-top: 5px; } .status { padding: 10px; margin: 10px 0; border-radius: 4px; background: #e3f2fd; color: #1976d2; } </style> </head> <body> <h1>PDQ Web Worker Example</h1> <p>This example demonstrates using pdq-wasm in a Web Worker for non-blocking image hashing.</p> <div class="container"> <h2>Select Images to Hash</h2> <input type="file" id="fileInput" accept="image/*" multiple> <p>Select one or more images. They will be hashed in a Web Worker without blocking the UI.</p> </div> <div id="status"></div> <div id="results"></div> <script> const fileInput = document.getElementById('fileInput'); const statusDiv = document.getElementById('status'); const resultsDiv = document.getElementById('results'); // Create a Web Worker const worker = new Worker('pdq-worker.js'); // Handle worker messages worker.onmessage = (event) => { const { type, hash, filename, error } = event.data; switch (type) { case 'ready': statusDiv.innerHTML = '<div class="status">✓ Worker ready</div>'; break; case 'hash-result': { const resultDiv = document.createElement('div'); resultDiv.className = 'hash-result'; const strong = document.createElement('strong'); strong.textContent = filename; const hashDiv = document.createElement('div'); hashDiv.className = 'hash'; hashDiv.textContent = hash; resultDiv.appendChild(strong); resultDiv.appendChild(hashDiv); resultsDiv.appendChild(resultDiv); break; } case 'error': { const errorDiv = document.createElement('div'); errorDiv.className = 'hash-result error'; const strong = document.createElement('strong'); strong.textContent = `Error${filename ? ': ' + filename : ''}`; const errorMsgDiv = document.createElement('div'); errorMsgDiv.textContent = error; errorDiv.appendChild(strong); errorDiv.appendChild(errorMsgDiv); resultsDiv.appendChild(errorDiv); break; } } }; worker.onerror = (error) => { console.error('Worker error:', error); statusDiv.innerHTML = '<div class="status" style="background: #ffebee; color: #c62828;">✗ Worker error: ' + error.message + '</div>'; }; // Initialize the worker first worker.postMessage({ type: 'init' }); // Handle file selection fileInput.addEventListener('change', async (event) => { const files = event.target.files; if (files.length === 0) return; resultsDiv.innerHTML = ''; statusDiv.innerHTML = `<div class="status">Processing ${files.length} image(s)...</div>`; // Send each file to the worker for (const file of files) { worker.postMessage({ type: 'hash', data: { file, filename: file.name } }); } }); </script> </body> </html>