| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const API_BASE = 'https://aarav13-authrix.hf.space'; |
| const CAPTURE_SEC = 8; |
| const OFFSCREEN_URL = chrome.runtime.getURL('offscreen.html'); |
|
|
| |
| chrome.runtime.onInstalled.addListener(() => { |
| chrome.contextMenus.create({ |
| id: 'authrix-capture', |
| title: 'π Analyze with Authrix', |
| contexts: ['page', 'video', 'frame'], |
| }); |
| chrome.contextMenus.create({ |
| id: 'authrix-url', |
| title: 'π Analyze video URL with Authrix', |
| contexts: ['link'], |
| }); |
| }); |
|
|
| chrome.contextMenus.onClicked.addListener((info, tab) => { |
| if (info.menuItemId === 'authrix-capture' && tab?.id) { |
| startCapture(tab.id); |
| } |
| if (info.menuItemId === 'authrix-url' && tab?.id && info.linkUrl) { |
| analyzeUrl(info.linkUrl, tab.id); |
| } |
| }); |
|
|
| |
| chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { |
|
|
| if (msg.type === 'START_CAPTURE') { |
| const tabId = msg.tabId || sender.tab?.id; |
| startCapture(tabId) |
| .then(() => sendResponse({ ok: true })) |
| .catch(e => sendResponse({ ok: false, error: e.message })); |
| return true; |
| } |
|
|
| if (msg.type === 'ANALYZE_URL') { |
| const tabId = msg.tabId || sender.tab?.id; |
| analyzeUrl(msg.url, tabId) |
| .then(() => sendResponse({ ok: true })) |
| .catch(e => sendResponse({ ok: false, error: e.message })); |
| return true; |
| } |
|
|
| if (msg.type === 'CHECK_HEALTH') { |
| fetch(`${API_BASE}/health`) |
| .then(r => r.json()) |
| .then(d => sendResponse({ ok: true, data: d })) |
| .catch(() => sendResponse({ ok: false })); |
| return true; |
| } |
|
|
| |
| if (msg.type === 'BLOB_READY') { |
| handleBlobReady(msg.chunks, msg.mimeType, msg.tabId, msg.totalSize); |
| return false; |
| } |
|
|
| if (msg.type === 'RECORD_PROGRESS') { |
| if (msg.tabId) { |
| chrome.tabs.sendMessage(msg.tabId, { |
| type: 'CAPTURE_PROGRESS', |
| elapsed: msg.elapsed, |
| total: msg.total, |
| }).catch(() => {}); |
| } |
| return false; |
| } |
|
|
| if (msg.type === 'RECORD_ERROR') { |
| if (msg.tabId) { |
| chrome.tabs.sendMessage(msg.tabId, { |
| type: 'ANALYSIS_ERROR', |
| error: msg.error, |
| }).catch(() => {}); |
| } |
| closeOffscreen(); |
| return false; |
| } |
| }); |
|
|
| |
| async function startCapture(tabId) { |
| if (!tabId) throw new Error('No tab ID'); |
|
|
| |
| await chrome.tabs.sendMessage(tabId, { type: 'SHOW_CAPTURE_OVERLAY' }).catch(() => { |
| |
| return chrome.scripting.executeScript({ |
| target: { tabId }, |
| files: ['content.js'], |
| }).then(() => |
| chrome.tabs.sendMessage(tabId, { type: 'SHOW_CAPTURE_OVERLAY' }) |
| ); |
| }); |
|
|
| |
| const streamId = await new Promise((resolve, reject) => { |
| chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, id => { |
| if (chrome.runtime.lastError) { |
| reject(new Error(chrome.runtime.lastError.message)); |
| } else { |
| resolve(id); |
| } |
| }); |
| }); |
|
|
| |
| await ensureOffscreen(); |
|
|
| |
| await chrome.runtime.sendMessage({ |
| type: 'RECORD_OFFSCREEN', |
| streamId, |
| durationMs: CAPTURE_SEC * 1000, |
| tabId, |
| }); |
| } |
|
|
| |
| async function analyzeUrl(url, tabId) { |
| if (!url) throw new Error('No URL provided'); |
|
|
| if (tabId) { |
| await chrome.tabs.sendMessage(tabId, { type: 'SHOW_URL_ANALYSIS_OVERLAY', url }).catch(() => {}); |
| } |
|
|
| try { |
| const res = await fetch(`${API_BASE}/analyze-url`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ url }), |
| }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({})); |
| throw new Error(err.detail || `Backend error ${res.status}`); |
| } |
|
|
| const result = await res.json(); |
| result.file = url.length > 60 ? url.slice(0, 57) + 'β¦' : url; |
|
|
| chrome.storage.local.set({ lastResult: result }); |
|
|
| if (tabId) { |
| chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_RESULT', result }).catch(() => {}); |
| } |
| } catch (err) { |
| if (tabId) { |
| chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_ERROR', error: err.message }).catch(() => {}); |
| } |
| throw err; |
| } |
| } |
|
|
| |
| async function handleBlobReady(chunks, mimeType, tabId, totalSize) { |
| closeOffscreen(); |
|
|
| try { |
| if (tabId) { |
| chrome.tabs.sendMessage(tabId, { type: 'CAPTURE_PROGRESS', elapsed: 12, total: 12 }).catch(() => {}); |
| } |
|
|
| const result = await submitBlob(chunks, mimeType, totalSize); |
| result.file = 'Tab capture'; |
|
|
| chrome.storage.local.set({ lastResult: result }); |
|
|
| if (tabId) { |
| chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_RESULT', result }).catch(() => {}); |
| } |
| } catch (err) { |
| if (tabId) { |
| chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_ERROR', error: err.message }).catch(() => {}); |
| } |
| } |
| } |
|
|
| |
| async function submitBlob(chunks, mimeType, totalSize) { |
| |
| const uint8Array = new Uint8Array(totalSize); |
| let offset = 0; |
| |
| for (const chunk of chunks) { |
| uint8Array.set(chunk, offset); |
| offset += chunk.length; |
| } |
|
|
| const blob = new Blob([uint8Array], { type: mimeType || 'video/webm' }); |
| const filename = `capture_${Date.now()}.webm`; |
|
|
| const fd = new FormData(); |
| fd.append('file', blob, filename); |
|
|
| const res = await fetch(`${API_BASE}/analyze`, { method: 'POST', body: fd }); |
| if (!res.ok) { |
| const err = await res.json().catch(() => ({})); |
| throw new Error(err.detail || `Backend error ${res.status}`); |
| } |
| return res.json(); |
| } |
|
|
| |
| async function ensureOffscreen() { |
| const existing = await chrome.offscreen.hasDocument().catch(() => false); |
| if (!existing) { |
| await chrome.offscreen.createDocument({ |
| url: OFFSCREEN_URL, |
| reasons: ['USER_MEDIA'], |
| justification: 'Record tab video stream for deepfake analysis', |
| }); |
| } |
| } |
|
|
| async function closeOffscreen() { |
| const exists = await chrome.offscreen.hasDocument().catch(() => false); |
| if (exists) { |
| await chrome.offscreen.closeDocument().catch(() => {}); |
| } |
| } |
|
|