Spaces:
Paused
Paused
File size: 1,714 Bytes
a920dd4 | 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 | // Audio CAPTCHA solver — Whisper running locally via @huggingface/transformers.
// No API key, no external service. Model files are pre-downloaded at build time.
//
// Best fit: reCAPTCHA v2 audio fallback (English).
let _pipePromise = null;
async function getPipeline() {
if (_pipePromise) return _pipePromise;
_pipePromise = (async () => {
const { pipeline, env } = await import('@huggingface/transformers');
// Cache + offline-friendly defaults
env.allowLocalModels = true;
env.cacheDir = process.env.TRANSFORMERS_CACHE || '/app/.cache/transformers';
// whisper-tiny.en is fast, English-only, ~40MB. Good for reCAPTCHA.
const model = process.env.WHISPER_MODEL || 'Xenova/whisper-tiny.en';
return pipeline('automatic-speech-recognition', model, {
quantized: true,
});
})();
return _pipePromise;
}
// Transcribe an audio buffer (Uint8Array or Buffer) → text.
async function transcribe(audioBuffer) {
const pipe = await getPipeline();
// The pipeline accepts a URL, a Buffer, or a Float32Array. Buffer is simplest.
const result = await pipe(new Uint8Array(audioBuffer), {
chunk_length_s: 30,
stride_length_s: 5,
});
return (result?.text || '').trim();
}
// Convenience: download the URL with the page's auth cookies & transcribe.
async function transcribeUrl(page, url) {
const buf = await page.evaluate(async (u) => {
const r = await fetch(u, { credentials: 'include' });
if (!r.ok) throw new Error('audio fetch failed: ' + r.status);
const ab = await r.arrayBuffer();
return Array.from(new Uint8Array(ab));
}, url);
return transcribe(Buffer.from(buf));
}
module.exports = { transcribe, transcribeUrl, getPipeline };
|