Auto-deploy: 2026-06-02 18:39:04
This commit is contained in:
117
speech.js
Normal file
117
speech.js
Normal file
@@ -0,0 +1,117 @@
|
||||
// speech.js - Handles webkitSpeechRecognition inside the injected iframe
|
||||
|
||||
let recognition = null;
|
||||
let isRecording = false;
|
||||
let localStream = null;
|
||||
|
||||
function stopMediaTracks() {
|
||||
if (localStream) {
|
||||
localStream.getTracks().forEach(track => track.stop());
|
||||
localStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
function initRecognition(language) {
|
||||
if (recognition) return recognition;
|
||||
|
||||
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||
if (!SpeechRecognition) {
|
||||
throw new Error('Speech recognition not supported');
|
||||
}
|
||||
|
||||
recognition = new SpeechRecognition();
|
||||
recognition.continuous = true;
|
||||
recognition.interimResults = true;
|
||||
recognition.lang = language || 'ar-SA';
|
||||
recognition.maxAlternatives = 1;
|
||||
|
||||
recognition.onresult = (event) => {
|
||||
let interimText = '';
|
||||
let finalText = '';
|
||||
|
||||
for (let i = event.resultIndex; i < event.results.length; i++) {
|
||||
const result = event.results[i];
|
||||
if (result.isFinal) {
|
||||
finalText += result[0].transcript + ' ';
|
||||
} else {
|
||||
interimText += result[0].transcript;
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'OFFSCREEN_RECORDING_RESULT',
|
||||
payload: { interimText, finalText }
|
||||
});
|
||||
};
|
||||
|
||||
recognition.onerror = (event) => {
|
||||
console.error('[Speech Iframe] Recognition error:', event.error);
|
||||
|
||||
if (event.error !== 'no-speech') {
|
||||
isRecording = false;
|
||||
stopMediaTracks();
|
||||
}
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'OFFSCREEN_RECORDING_ERROR',
|
||||
payload: { error: event.error }
|
||||
});
|
||||
};
|
||||
|
||||
recognition.onend = () => {
|
||||
if (isRecording) {
|
||||
try {
|
||||
recognition.start();
|
||||
} catch (e) {
|
||||
console.warn('[Speech Iframe] Restart failed:', e);
|
||||
}
|
||||
} else {
|
||||
stopMediaTracks();
|
||||
chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_END' });
|
||||
}
|
||||
};
|
||||
|
||||
return recognition;
|
||||
}
|
||||
|
||||
// Listen for messages broadcasted across the extension
|
||||
chrome.runtime.onMessage.addListener((message) => {
|
||||
if (message.type === 'START_RECORDING_FROM_POPUP') {
|
||||
const lang = message.payload?.language || 'ar-SA';
|
||||
|
||||
navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
.then((stream) => {
|
||||
localStream = stream;
|
||||
try {
|
||||
if (!recognition || recognition.lang !== lang) {
|
||||
recognition = initRecognition(lang);
|
||||
}
|
||||
if (!isRecording) {
|
||||
recognition.start();
|
||||
isRecording = true;
|
||||
// Tell popup it started successfully
|
||||
chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_START_SUCCESS' });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Speech Iframe] Failed to start:', e);
|
||||
chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_ERROR', payload: { error: e.message } });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[Speech Iframe] getUserMedia failed:', err);
|
||||
chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_ERROR', payload: { error: 'not-allowed' } });
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'STOP_RECORDING_FROM_POPUP') {
|
||||
if (isRecording && recognition) {
|
||||
isRecording = false;
|
||||
try {
|
||||
recognition.stop();
|
||||
} catch (e) {
|
||||
console.warn('[Speech Iframe] Stop failed:', e);
|
||||
}
|
||||
}
|
||||
stopMediaTracks();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user