Files
WhisperLiveKit/chrome-extension/service-worker.js
2025-09-04 16:36:28 +02:00

250 lines
7.9 KiB
JavaScript

console.log("Service worker loaded");
let isRecording = false;
let currentStreamId = null;
chrome.runtime.onInstalled.addListener((details) => {
console.log("Extension installed/updated");
});
chrome.action.onClicked.addListener((tab) => {
// Get the current tab ID
const tabId = tab.id;
// Inject the content script into the current tab
chrome.scripting.executeScript({
target: { tabId: tabId },
files: ['style_popup.js']
});
console.log(`Content script injected into tab ${tabId}`);
});
// Handle messages from popup
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
console.log("Service worker received message:", message);
try {
switch (message.type) {
case 'start-capture':
const startResult = await startTabCapture(message.tabId, message.websocketUrl);
sendResponse(startResult);
break;
case 'stop-capture':
const stopResult = await stopTabCapture();
sendResponse(stopResult);
break;
case 'get-recording-state':
sendResponse({ isRecording: isRecording });
break;
default:
sendResponse({ success: false, error: 'Unknown message type' });
}
} catch (error) {
console.error('Error handling message:', error);
sendResponse({ success: false, error: error.message });
}
return true; // Keep message channel open for async response
});
async function startTabCapture(tabId, websocketUrl) {
console.log('Service worker: Starting tab capture process...');
console.log('Service worker: tabId:', tabId, 'websocketUrl:', websocketUrl);
try {
if (isRecording) {
console.log('Service worker: Already recording, aborting');
return { success: false, error: 'Already recording' };
}
// Check if offscreen document exists
console.log('Service worker: Checking for existing offscreen document...');
const existingContexts = await chrome.runtime.getContexts({});
console.log('Service worker: Found contexts:', existingContexts.length);
const offscreenDocument = existingContexts.find(
(c) => c.contextType === 'OFFSCREEN_DOCUMENT'
);
console.log('Service worker: Offscreen document exists:', !!offscreenDocument);
// Create offscreen document if it doesn't exist
if (!offscreenDocument) {
console.log('Service worker: Creating offscreen document...');
try {
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['USER_MEDIA'],
justification: 'Capturing and processing tab audio for transcription'
});
console.log('Service worker: Offscreen document created successfully');
// Wait for offscreen document to initialize
console.log('Service worker: Waiting for offscreen document to initialize...');
await new Promise(resolve => setTimeout(resolve, 500));
console.log('Service worker: Offscreen document initialization delay complete');
} catch (offscreenError) {
console.error('Service worker: Failed to create offscreen document:', offscreenError);
return { success: false, error: 'Failed to create offscreen document: ' + offscreenError.message };
}
}
// Get media stream ID for the tab
console.log('Service worker: Getting media stream ID for tab:', tabId);
try {
currentStreamId = await chrome.tabCapture.getMediaStreamId({
targetTabId: tabId
});
console.log('Service worker: Media stream ID:', currentStreamId);
} catch (tabCaptureError) {
console.error('Service worker: Failed to get media stream ID:', tabCaptureError);
return { success: false, error: 'Failed to get media stream ID: ' + tabCaptureError.message };
}
if (!currentStreamId) {
console.log('Service worker: No media stream ID returned');
return { success: false, error: 'Failed to get media stream ID - no stream returned' };
}
// Send message to offscreen document to start capture with retry logic
console.log('Service worker: Sending start message to offscreen document...');
let response;
let retryCount = 0;
const maxRetries = 5;
while (!response && retryCount < maxRetries) {
try {
console.log(`Service worker: Attempt ${retryCount + 1}/${maxRetries} to communicate with offscreen document`);
// Send message to offscreen document without target property
response = await chrome.runtime.sendMessage({
type: 'start-recording',
target: 'offscreen',
data: {
streamId: currentStreamId,
websocketUrl: websocketUrl
}
});
if (!response) {
console.warn(`Service worker: No response from offscreen document, waiting before retry...`);
await new Promise(resolve => setTimeout(resolve, 200));
retryCount++;
} else {
console.log(`Service worker: Successfully communicated with offscreen document on attempt ${retryCount + 1}`);
}
} catch (sendError) {
console.error(`Service worker: Error sending message to offscreen document (attempt ${retryCount + 1}):`, sendError);
response = { success: false, error: 'Failed to communicate with offscreen document: ' + sendError.message };
break;
}
}
console.log('Service worker: Final offscreen document response:', response);
if (response && response.success) {
isRecording = true;
console.log('Service worker: Recording started successfully');
// Notify popup of state change
try {
chrome.runtime.sendMessage({
type: 'recording-state',
isRecording: true
});
} catch (e) {
console.warn('Service worker: Could not notify popup of state change:', e);
}
return { success: true };
} else {
console.log('Service worker: Offscreen document returned failure');
return { success: false, error: response?.error || 'Failed to start recording in offscreen document' };
}
} catch (error) {
console.error('Service worker: Exception in startTabCapture:', error);
return { success: false, error: 'Exception: ' + error.message };
}
}
async function stopTabCapture() {
try {
if (!isRecording) {
return { success: false, error: 'Not currently recording' };
}
// Send message to offscreen document to stop capture
const response = await chrome.runtime.sendMessage({
type: 'stop-recording',
target: 'offscreen'
});
isRecording = false;
currentStreamId = null;
// Notify popup of state change
try {
chrome.runtime.sendMessage({
type: 'recording-state',
isRecording: false
});
} catch (e) {
// Popup might be closed, ignore error
}
return { success: true };
} catch (error) {
console.error('Error stopping tab capture:', error);
isRecording = false;
currentStreamId = null;
return { success: false, error: error.message };
}
}
// Handle messages from offscreen document
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.target === 'service-worker') {
switch (message.type) {
case 'recording-stopped':
isRecording = false;
currentStreamId = null;
// Notify popup
try {
chrome.runtime.sendMessage({
type: 'recording-state',
isRecording: false
});
} catch (e) {
// Popup might be closed, ignore error
}
break;
case 'recording-error':
isRecording = false;
currentStreamId = null;
// Notify popup
try {
chrome.runtime.sendMessage({
type: 'status-update',
status: 'error',
message: message.error || 'Recording error occurred'
});
} catch (e) {
// Popup might be closed, ignore error
}
break;
}
}
});