95 lines
2.9 KiB
JavaScript
95 lines
2.9 KiB
JavaScript
// ============================================================
|
|
// Volume Booster — Offscreen Document
|
|
// Läuft in einem unsichtbaren DOM-Kontext. Nur hier sind
|
|
// getUserMedia + AudioContext verfügbar. Hält pro Tab einen
|
|
// GainNode und gibt den verstärkten Stream wieder aus.
|
|
// ============================================================
|
|
|
|
// Map<tabId, { context, gain, source, stream }>
|
|
const active = new Map();
|
|
|
|
function teardown(tabId) {
|
|
const entry = active.get(tabId);
|
|
if (!entry) return;
|
|
try {
|
|
entry.source.disconnect();
|
|
entry.gain.disconnect();
|
|
entry.stream.getTracks().forEach((t) => t.stop());
|
|
entry.context.close();
|
|
} catch (e) {
|
|
/* trotzdem aufräumen */
|
|
}
|
|
active.delete(tabId);
|
|
}
|
|
|
|
async function applyBoost(tabId, streamId, value) {
|
|
// Falls (z. B. nach Fehler) noch ein alter Eintrag existiert: erst sauber weg.
|
|
if (active.has(tabId)) {
|
|
teardown(tabId);
|
|
}
|
|
|
|
// Stream aus der ID erzeugen
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
audio: {
|
|
mandatory: {
|
|
chromeMediaSource: "tab",
|
|
chromeMediaSourceId: streamId,
|
|
},
|
|
},
|
|
video: false,
|
|
});
|
|
|
|
const context = new AudioContext();
|
|
const source = context.createMediaStreamSource(stream);
|
|
const gain = context.createGain();
|
|
gain.gain.value = value;
|
|
|
|
source.connect(gain);
|
|
gain.connect(context.destination); // Audio bleibt für den Nutzer hörbar
|
|
|
|
// Wenn der Stream endet (Tab neu geladen / geschlossen)
|
|
const track = stream.getAudioTracks()[0];
|
|
if (track) {
|
|
track.addEventListener("ended", () => teardown(tabId));
|
|
}
|
|
|
|
active.set(tabId, { context, gain, source, stream });
|
|
return { ok: true, boosting: true, value };
|
|
}
|
|
|
|
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
if (msg.target !== "offscreen") return false;
|
|
|
|
(async () => {
|
|
try {
|
|
if (msg.type === "APPLY") {
|
|
const res = await applyBoost(msg.tabId, msg.streamId, msg.value);
|
|
sendResponse(res);
|
|
} else if (msg.type === "GAIN") {
|
|
// Nur die Lautstärke eines bereits aktiven Tabs ändern
|
|
const entry = active.get(msg.tabId);
|
|
if (entry) {
|
|
entry.gain.gain.value = msg.value;
|
|
sendResponse({ ok: true, boosting: true, value: msg.value });
|
|
} else {
|
|
// Kein aktiver Stream (mehr) — Popup soll neu anwenden
|
|
sendResponse({ ok: false, error: "no active stream", boosting: false });
|
|
}
|
|
} else if (msg.type === "STOP") {
|
|
teardown(msg.tabId);
|
|
sendResponse({ ok: true, boosting: false });
|
|
} else if (msg.type === "STATE") {
|
|
const entry = active.get(msg.tabId);
|
|
sendResponse({
|
|
ok: true,
|
|
boosting: !!entry,
|
|
value: entry ? entry.gain.gain.value : 1,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
sendResponse({ ok: false, error: String((err && err.message) || err) });
|
|
}
|
|
})();
|
|
|
|
return true; // async
|
|
});
|