Church Translate, as it runs at the campuses
Live sermon translation into 100+ languages, running entirely on one Mac per campus: audio in, voice detection, Whisper on the GPU, NLLB-200 in its own process, and a built-in web server the congregation reads from on their phones. No audio leaves the building. This is the source only. What used to sit beside it — 14 GB of speech and translation models, 8 GB of voice bundles, 6 GB of natural voices, the Electron caches, and the app data holding the operator PIN and every service transcript — is machine state and downloaded assets, and stays in the NAS backup where it belongs. The vendored node, cloudflared and Windows ffmpeg binaries are out too (533 MB, re-fetched for packaging); the browser-side runtime beside them is kept, so a checkout still serves the congregation page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/* The whole chain as engine.js runs it: split -> liturgy-or-NLLB -> guard. */
|
||||
import { pipeline, env } from '@huggingface/transformers';
|
||||
import { createRequire } from 'module';
|
||||
import os from 'os'; import path from 'path'; import fs from 'fs'; import vm from 'vm';
|
||||
const require = createRequire(import.meta.url);
|
||||
const Liturgy = require('./liturgy.js');
|
||||
const Guard = require('./mt-guard.js');
|
||||
|
||||
const src = fs.readFileSync('engine.js','utf8');
|
||||
const sb={console}; vm.createContext(sb);
|
||||
vm.runInContext(src.match(/^const ABBREV_TAIL = .*$/m)[0], sb);
|
||||
const grab=(n)=>{const i=src.indexOf('function '+n+'(');let d=0;for(let k=src.indexOf('{',i);k<src.length;k++){if(src[k]==='{')d++;else if(src[k]==='}'&&--d===0)return src.slice(i,k+1);}};
|
||||
vm.runInContext(grab('splitSentences'), sb);
|
||||
|
||||
env.allowLocalModels=false;
|
||||
env.cacheDir=path.join(os.homedir(),'Library/Application Support/Church Translate Models');
|
||||
const pipe=await pipeline('translation','Xenova/nllb-200-distilled-600M',{dtype:'fp32'});
|
||||
|
||||
const CASES=['God is good. Amen.','Amen.','Hallelujah.','Donald Trump.',
|
||||
'Can I get an amen?','Jesus wept. Amen.','Dr. Luke wrote this. Amen.'];
|
||||
const LANGS=['spa_Latn','fra_Latn','deu_Latn','zho_Hans'];
|
||||
|
||||
for (const text of CASES) {
|
||||
console.log('\n"'+text+'" -> '+JSON.stringify(sb.splitSentences(text)));
|
||||
for (const lang of LANGS) {
|
||||
const parts=[];
|
||||
for (const s of sb.splitSentences(text)) {
|
||||
const set=Liturgy.lookup(s,lang);
|
||||
if (set) { parts.push(set+' [liturgy]'); continue; }
|
||||
const o=await pipe(s,{src_lang:'eng_Latn',tgt_lang:lang});
|
||||
const raw=(Array.isArray(o)?o[0]:o).translation_text.trim();
|
||||
const g=Guard.clean(s,raw);
|
||||
parts.push(g.text + (g.trimmed?' [guard: '+g.reason.slice(0,40)+']':''));
|
||||
}
|
||||
console.log(' '+lang+' '+parts.join(' | '));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user