Skip to content

Instantly share code, notes, and snippets.

@chenbojian
Created February 3, 2026 07:05
Show Gist options
  • Select an option

  • Save chenbojian/2d76d269d9a3cd6c857c380cf03fae85 to your computer and use it in GitHub Desktop.

Select an option

Save chenbojian/2d76d269d9a3cd6c857c380cf03fae85 to your computer and use it in GitHub Desktop.
playwright to inject audio into page as microphone input
import base64
import os
# MIME by extension for common audio formats
_AUDIO_MIME = {
".wav": "audio/wav",
".mp3": "audio/mpeg",
".ogg": "audio/ogg",
".webm": "audio/webm",
".m4a": "audio/mp4",
".aac": "audio/aac",
}
async def inject_audio(page, audio_file):
"""
Replace getUserMedia({ audio: true }) with a MediaStream from the given audio file.
Call this after page.goto() so the page loads with normal fingerprint first.
"""
path = os.path.abspath(os.path.expanduser(audio_file))
ext = os.path.splitext(path)[1].lower()
mime = _AUDIO_MIME.get(ext, "application/octet-stream")
with open(path, "rb") as f:
raw = f.read()
b64 = base64.b64encode(raw).decode("ascii")
data_url = f"data:{mime};base64,{b64}"
payload = {"audioDataUrl": data_url}
js = r"""
(payload) => {
if (!payload || !payload.audioDataUrl) return;
const audioDataUrl = payload.audioDataUrl;
const originalGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = async function(constraints) {
if (constraints && constraints.audio) {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const resp = await fetch(audioDataUrl);
const ab = await resp.arrayBuffer();
const decoded = await ctx.decodeAudioData(ab);
const dest = ctx.createMediaStreamDestination();
const source = ctx.createBufferSource();
source.buffer = decoded;
source.connect(dest);
source.start(0);
const silentBuf = ctx.createBuffer(1, ctx.sampleRate, ctx.sampleRate);
const silentSource = ctx.createBufferSource();
silentSource.buffer = silentBuf;
silentSource.loop = true;
silentSource.connect(dest);
silentSource.start(decoded.duration);
const stream = dest.stream;
stream.getAudioTracks().forEach(t => { t._mock = true; });
return Promise.resolve(stream);
}
return originalGetUserMedia(constraints);
};
}
"""
await page.evaluate(js, payload)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment