Deepfake Authenticator
feat: speed optimizations, confidence calibration, pricing page, HF deployment ready
7902802 | /** | |
| * Authrix Extension — Offscreen Document | |
| * | |
| * Offscreen documents can use getUserMedia with chromeMediaSource:'tab', | |
| * which is NOT available in content scripts or service workers in MV3. | |
| */ | |
| chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { | |
| if (msg.type === 'RECORD_OFFSCREEN') { | |
| startRecording(msg.streamId, msg.durationMs, msg.tabId) | |
| .then(() => sendResponse({ ok: true })) | |
| .catch(e => sendResponse({ ok: false, error: e.message })); | |
| return true; | |
| } | |
| }); | |
| let activeRecorder = null; | |
| async function startRecording(streamId, durationMs, tabId) { | |
| if (activeRecorder && activeRecorder.state === 'recording') { | |
| activeRecorder.stop(); | |
| } | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { | |
| mandatory: { | |
| chromeMediaSource: 'tab', | |
| chromeMediaSourceId: streamId, | |
| }, | |
| }, | |
| audio: { | |
| mandatory: { | |
| chromeMediaSource: 'tab', | |
| chromeMediaSourceId: streamId, | |
| }, | |
| }, | |
| }); | |
| const mimeType = getSupportedMimeType(); | |
| const recorder = new MediaRecorder(stream, { | |
| mimeType, | |
| videoBitsPerSecond: 4_000_000, // 4Mbps — good quality, smaller file | |
| }); | |
| activeRecorder = recorder; | |
| const chunks = []; | |
| recorder.ondataavailable = e => { | |
| if (e.data && e.data.size > 0) chunks.push(e.data); | |
| }; | |
| recorder.onstop = async () => { | |
| stream.getTracks().forEach(t => t.stop()); | |
| try { | |
| const blob = new Blob(chunks, { type: mimeType }); | |
| // Convert blob to ArrayBuffer (more reliable than base64 for large files) | |
| const arrayBuffer = await blob.arrayBuffer(); | |
| const uint8Array = new Uint8Array(arrayBuffer); | |
| // Chunk into 1MB pieces to avoid message size limits | |
| const CHUNK_SIZE = 1024 * 1024; // 1MB | |
| const totalChunks = Math.ceil(uint8Array.length / CHUNK_SIZE); | |
| const chunkedData = []; | |
| for (let i = 0; i < totalChunks; i++) { | |
| const start = i * CHUNK_SIZE; | |
| const end = Math.min(start + CHUNK_SIZE, uint8Array.length); | |
| const chunk = uint8Array.slice(start, end); | |
| // Convert to regular array for JSON serialization | |
| chunkedData.push(Array.from(chunk)); | |
| } | |
| chrome.runtime.sendMessage({ | |
| type: 'BLOB_READY', | |
| chunks: chunkedData, | |
| mimeType, | |
| tabId, | |
| totalSize: uint8Array.length, | |
| }); | |
| } catch (err) { | |
| chrome.runtime.sendMessage({ | |
| type: 'RECORD_ERROR', | |
| error: err.message, | |
| tabId, | |
| }); | |
| } | |
| }; | |
| recorder.onerror = e => { | |
| chrome.runtime.sendMessage({ | |
| type: 'RECORD_ERROR', | |
| error: e.error?.message || 'MediaRecorder error', | |
| tabId, | |
| }); | |
| }; | |
| recorder.start(1000); | |
| setTimeout(() => { | |
| if (recorder.state === 'recording') recorder.stop(); | |
| }, durationMs); | |
| // Progress ticks | |
| const startTime = Date.now(); | |
| const tick = setInterval(() => { | |
| if (recorder.state !== 'recording') { clearInterval(tick); return; } | |
| const elapsed = Math.round((Date.now() - startTime) / 1000); | |
| const total = Math.round(durationMs / 1000); | |
| chrome.runtime.sendMessage({ type: 'RECORD_PROGRESS', elapsed, total, tabId }); | |
| }, 800); | |
| } | |
| function getSupportedMimeType() { | |
| const types = [ | |
| 'video/webm;codecs=vp9,opus', | |
| 'video/webm;codecs=vp8,opus', | |
| 'video/webm', | |
| 'video/mp4', | |
| ]; | |
| return types.find(t => MediaRecorder.isTypeSupported(t)) || 'video/webm'; | |
| } | |