mirror of
https://github.com/QuentinFuxa/WhisperLiveKit.git
synced 2026-03-07 22:33:36 +00:00
250 lines
7.9 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
});
|