まとめ
declarativeNetRequestでOriginを削除することで、利用者に手間をかけさせずVOICEVOXのAPIを利用することができる。
VOICEVOXのissueにもそういった対策が書かれているが、気づきにくいので本記事を書いて知見として残しておく。
またオーディオの再生まわりでも色々な制限が出て来るのでそれについても書いておく。
解説
VOICEVOX側での制限
VOICEVOXのエンジンではCORSの範囲の範囲を制限しておりそのままではブラウザ拡張からのAPI呼び出しが弾かれる。
一応エンジン側にて許可するOriginを追加することができるようになっているが、利用者に chrome-extension://...
を追加してもらうというのは手間がかかるし好ましくない。
declarativeNetRequest
Chrome拡張側でfetch等のリクエストに対して細工を行うための機能が存在する。こちらで細工を行ってVOICEVOXが通してくれるリクエストを投げるようにする。
これによってbackgroundからVOICEVOXのAPI呼び出しが通るようになる。
設定例
{
"permissions": [
"declarativeNetRequest"
],
"host_permissions": [
"*://localhost/*"
],
"declarative_net_request": {
"rule_resources": [
{
"id": "ruleset",
"enabled": true,
"path": "rules.json"
}
]
}
}
[
{
"id": 1,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{ "header": "Origin", "operation": "remove" }
]
},
"condition": {
"urlFilter": "localhost"
}
}
]
backgroundではAudioContextが利用できない
ブラウザで音声を再生するにはAudioContextを利用するのがお手軽そう。
しかしbackgroundはManifest V3からServiceWorkerとなっているためページを持たなくなり、AudioContextも利用できなくなっている。
そのためoffscreenだったりcontent_scriptsを活用してページ側で音声を再生する必要がある。やり取りにはsendMessageを利用する。
ちなみに最初からページ側でAPIリクエストも行っていくというのはOriginヘッダの除去がcontent_scriptsでは機能しないためダメです。(offscreenは未検証)
メッセージはJSON対応のオブジェクトである必要がある
ArrayBufferをそのまま送ろうとしても失敗する。sendMessageはJSONとして取り扱えるプリミティブなデータしか取り扱えない。そのため送受信で変換する必要がある。
Array.from(new Uint8Array(arrayBuffer));
const arrayBuffer = Uint8Array.from(array).buffer;
let currentSourceNode;
const context = new AudioContext();
const gainNode = context.createGain();
gainNode.connect(context.destination);
context.decodeAudioData(arrayBuffer).then(decoded => {
if (currentSourceNode) {
currentSourceNode.stop();
currentSourceNode.disconnect();
}
const sourceNode = context.createBufferSource();
sourceNode.connect(gainNode);
sourceNode.buffer = decoded;
sourceNode.start(0);
currentSourceNode = sourceNode;
});