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,57 @@
|
||||
/*
|
||||
* preload.js — the only bridge between the renderer and Node.
|
||||
* Nothing here takes a path or a command from the page; every call is a fixed
|
||||
* verb with no free-form arguments beyond a config patch.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('engineHost', {
|
||||
getConfig: () => ipcRenderer.invoke('config:get'),
|
||||
setConfig: (patch) => ipcRenderer.invoke('config:set', patch),
|
||||
micStatus: () => ipcRenderer.invoke('mic:status'),
|
||||
askMic: () => ipcRenderer.invoke('mic:ask'),
|
||||
openPrivacy: () => ipcRenderer.invoke('app:openPrivacy'),
|
||||
cacheInfo: () => ipcRenderer.invoke('app:cacheInfo'),
|
||||
revealCache: () => ipcRenderer.invoke('app:revealCache'),
|
||||
copyText: (text) => ipcRenderer.invoke('app:copy', text),
|
||||
setLive: (live) => ipcRenderer.send('engine:live', !!live),
|
||||
logLine: (line) => ipcRenderer.send('log:line', String(line || '')),
|
||||
|
||||
/* Translation lives in the main process; these are its only two verbs. */
|
||||
mtEnsure: () => ipcRenderer.invoke('mt:ensure'),
|
||||
settingsExport: () => ipcRenderer.invoke('settings:export'),
|
||||
settingsImport: () => ipcRenderer.invoke('settings:import'),
|
||||
audioDevices: () => ipcRenderer.invoke('audio:devices'),
|
||||
modelsCached: (id) => ipcRenderer.invoke('models:cached', id),
|
||||
mtTranslate: (req) => ipcRenderer.invoke('mt:translate', req),
|
||||
mtTranslateMulti: (req) => ipcRenderer.invoke('mt:translateMulti', req),
|
||||
onMtProgress: (fn) => ipcRenderer.on('mt:progress', (_e, p) => fn(p)),
|
||||
onMtDown: (fn) => ipcRenderer.on('mt:down', (_e, info) => fn(info)),
|
||||
|
||||
/* The congregation server runs inside this app; these describe it. */
|
||||
localInfo: () => ipcRenderer.invoke('local:info'),
|
||||
onServerUp: (fn) => ipcRenderer.on('server:up', (_e, info) => fn(info)),
|
||||
onServerDown: (fn) => ipcRenderer.on('server:down', () => fn()),
|
||||
|
||||
/* Share to the internet: a Cloudflare quick tunnel for livestream viewers. */
|
||||
tunnelStart: () => ipcRenderer.invoke('tunnel:start'),
|
||||
tunnelStop: () => ipcRenderer.invoke('tunnel:stop'),
|
||||
tunnelStatus: () => ipcRenderer.invoke('tunnel:status'),
|
||||
onTunnel: (fn) => ipcRenderer.on('tunnel:status', (_e, s) => fn(s)),
|
||||
|
||||
/* Built-in voices: this Mac speaks, and the phones play what it sends. */
|
||||
ttsInfo: () => ipcRenderer.invoke('tts:info'),
|
||||
ttsSpeak: (req) => ipcRenderer.invoke('tts:speak', req),
|
||||
ttsPreload: (langs) => ipcRenderer.invoke('tts:preload', langs),
|
||||
/* Audition one specific voice, and save the per-language choices. */
|
||||
ttsAudition: (req) => ipcRenderer.invoke('tts:audition', req),
|
||||
ttsSetVoices: (c) => ipcRenderer.invoke('tts:setVoices', c),
|
||||
ttsSetListenerVoices: (m) => ipcRenderer.invoke('tts:setListenerVoices', m),
|
||||
onTtsReady: (fn) => ipcRenderer.on('tts:ready', (_e, i) => fn(i)),
|
||||
onTtsProgress: (fn) => ipcRenderer.on('tts:progress', (_e, p) => fn(p)),
|
||||
onTtsCloud: (fn) => ipcRenderer.on('tts:cloud', (_e, s) => fn(s)),
|
||||
onLocalVoices: (fn) => ipcRenderer.on('localvoices:status', (_e, s) => fn(s)),
|
||||
localVoicesStatus: () => ipcRenderer.invoke('localvoices:status')
|
||||
});
|
||||
Reference in New Issue
Block a user