-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathaudio-manager.js
More file actions
82 lines (70 loc) · 2.79 KB
/
audio-manager.js
File metadata and controls
82 lines (70 loc) · 2.79 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
export class AudioManager {
constructor() {
this.audioCache = new Map(); // index -> Promise<BlobUrl>
this.sentences = [];
this.abortController = new AbortController();
this.requestQueue = Promise.resolve(); // For sequential processing
}
clear() {
this.audioCache.clear();
this.sentences = [];
if (this.abortController) this.abortController.abort();
this.abortController = new AbortController();
this.requestQueue = Promise.resolve();
}
setSentences(sentences) {
this.sentences = sentences;
}
async getAudio(index) {
if (index >= this.sentences.length) return null;
if (this.audioCache.has(index)) {
return this.audioCache.get(index);
}
const promise = this.fetchAudio(this.sentences[index].text);
this.audioCache.set(index, promise);
return promise;
}
prefetch(index) {
if (index < this.sentences.length && !this.audioCache.has(index)) {
this.getAudio(index);
}
}
async fetchAudio(text) {
const data = await browser.storage.local.get(['pendingVoice', 'pendingApiUrl', 'pendingNormalizationOptions']);
// Use queue to ensure sequential requests to the API
// Catching here ensures the chain doesn't break for subsequent requests
return this.requestQueue = this.requestQueue.catch(() => { }).then(async () => {
try {
if (!data.pendingApiUrl) {
throw new Error("Missing API URL");
}
const endpoint = new URL('audio/speech', data.pendingApiUrl).href;
const result = await browser.runtime.sendMessage({
action: "FETCH_TTS_AUDIO",
endpoint: endpoint,
payload: {
model: 'kokoro',
input: text,
voice: data.pendingVoice,
response_format: 'mp3',
speed: 1.0,
normalization_options: data.pendingNormalizationOptions
}
});
if (!result || !result.success) {
throw new Error(result?.error || "Background fetch failed");
}
return result.dataUrl;
} catch (e) {
if (e.name === 'AbortError') throw e;
let msg = e.message;
if (msg === "Failed to fetch") {
const url = data.pendingApiUrl || "unknown URL";
msg = `Connection failed. Is Kokoro-FastAPI running on ${url}?`;
}
console.error("Kokoro Fetch failed:", e);
throw new Error(msg);
}
});
}
}