-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoffscreen.js
More file actions
160 lines (135 loc) · 6.49 KB
/
offscreen.js
File metadata and controls
160 lines (135 loc) · 6.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Offscreen document for making Gemini API calls
// This runs in a hidden page context with fewer restrictions than service workers
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'offscreen-gemini-transcribe') {
const { apiKey, audioBase64, systemInstruction } = request;
console.log('Offscreen: Received transcription request, audio length:', audioBase64?.length);
fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{
parts: [{
inline_data: {
mime_type: "audio/wav",
data: audioBase64
}
}]
}],
systemInstruction: {
parts: [{ text: systemInstruction }]
},
generationConfig: {
temperature: 0.0,
topP: 1.0,
topK: 1
}
})
}
)
.then(response => {
console.log('Offscreen: Got response, status:', response.status);
if (!response.ok) {
return response.text().then(errorText => {
throw new Error(`API error (${response.status}): ${errorText}`);
});
}
return response.json();
})
.then(data => {
const candidate = data.candidates?.[0];
const text = candidate?.content?.parts?.[0]?.text;
console.log('Offscreen: API Response Data:', JSON.stringify(data));
if (!text && data.promptFeedback) {
console.warn('Offscreen: Prompt was blocked by safety filters:', data.promptFeedback);
} else if (!text && candidate?.finishReason) {
console.warn('Offscreen: Candidate was blocked or failed. Finish reason:', candidate.finishReason);
}
if (text) {
console.log('Offscreen: Transcribed text:', text);
sendResponse({ success: true, text: text.trim(), data });
} else {
console.warn('Offscreen: No text found in API response');
sendResponse({
success: true,
text: '',
data,
warning: 'No transcription generated. Speech might be too quiet or unclear.'
});
}
})
.catch(error => {
console.error('Offscreen: Gemini API error:', error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep message channel open for async response
}
if (request.action === 'offscreen-chirp-transcribe') {
const { apiKey, projectId, region, recognizerId, audioBase64 } = request;
console.log('Offscreen: Received Chirp transcription request');
// Check for Service Account credentials in sync storage (matching options.js)
chrome.storage.sync.get(['serviceAccountCreds'], async (result) => {
let authHeader = {};
let finalApiKey = apiKey;
try {
if (result.serviceAccountCreds && result.serviceAccountCreds.private_key) {
console.log('Offscreen: Using Service Account for authentication');
// Initialize TokenService if not already done
if (!window.tokenService) {
window.tokenService = new TokenService(result.serviceAccountCreds);
}
const accessToken = await window.tokenService.getAccessToken();
authHeader = { 'Authorization': `Bearer ${accessToken}` };
// When using OAuth token, API key is not strictly needed but good for quota tracking
// If no API key provided, we rely solely on the token
} else if (!apiKey) {
throw new Error('No API Key or Service Account credentials found.');
} else {
console.log('Offscreen: Using API Key for authentication');
}
// Construct V2 API URL
let url = `https://speech.googleapis.com/v2/projects/${projectId}/locations/${region}/recognizers/${recognizerId}:recognize`;
if (finalApiKey) {
url += `?key=${finalApiKey}`;
}
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...authHeader
},
body: JSON.stringify({
config: {
autoDecodingConfig: {},
model: "chirp"
},
content: audioBase64
})
});
console.log('Offscreen: Got Chirp response, status:', response.status);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Chirp API error (${response.status}): ${errorText}`);
}
const data = await response.json();
// Extract text from V2 response structure
const results = data.results || [];
let text = '';
for (const result of results) {
if (result.alternatives && result.alternatives[0]) {
text += result.alternatives[0].transcript + ' ';
}
}
console.log('Offscreen: Chirp Transcribed text:', text);
sendResponse({ success: true, text: text.trim(), data });
} catch (error) {
console.error('Offscreen: Chirp API error:', error);
sendResponse({ success: false, error: error.message });
}
});
return true;
}
});
console.log('Offscreen document loaded and ready');