From f0afa200b11491691c4118b48121e9ff180cbda1 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Thu, 26 Feb 2026 22:56:51 -0500 Subject: [PATCH] Align live captions transport to flat language payloads --- routes/liveCaptions.js | 43 ++++++++++++++++++++++++++----- scripts/liveCaptionsTestSender.js | 32 +++++++++-------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/routes/liveCaptions.js b/routes/liveCaptions.js index 03da2f9..04490d6 100644 --- a/routes/liveCaptions.js +++ b/routes/liveCaptions.js @@ -6,6 +6,7 @@ const sessionChecker = require("../middleware/sessionChecker.js"); const MAX_BUFFER_SIZE = 300; const DEFAULT_INITIAL_LIMIT = 40; const MAX_INITIAL_LIMIT = 120; +const CAPTION_META_KEYS = new Set(["sequence", "createdAt", "original"]); const liveCaptionState = { startedAt: Date.now(), @@ -32,12 +33,36 @@ const normalizeTranslations = (translations) => { return normalized; }; +const buildTranslationsFromFlatPayload = (payload) => { + const ignoredKeys = new Set(["original", "sourceLang", "translations"]); + const normalized = {}; + for (const [key, value] of Object.entries(payload || {})) { + if (ignoredKeys.has(key)) continue; + const lang = normalizeLang(key); + const text = typeof value === "string" ? value.trim() : ""; + if (!lang || !text) continue; + normalized[lang] = text; + } + return normalized; +}; + +const inferSourceLangFromTranslations = (original, translations) => { + const normalizedOriginal = String(original || "").trim(); + if (!normalizedOriginal) return "original"; + for (const [lang, text] of Object.entries(translations || {})) { + if (String(text || "").trim() === normalizedOriginal) return normalizeLang(lang); + } + return "original"; +}; + const getAvailableLanguages = () => { const langs = new Set(); for (const caption of liveCaptionState.captions) { - if (caption?.sourceLang) langs.add(caption.sourceLang); - const translationMap = caption?.translations || {}; - Object.keys(translationMap).forEach((lang) => langs.add(normalizeLang(lang))); + Object.keys(caption || {}).forEach((key) => { + if (CAPTION_META_KEYS.has(key)) return; + const lang = normalizeLang(key); + if (lang) langs.add(lang); + }); } return Array.from(langs).filter(Boolean).sort(); }; @@ -79,20 +104,24 @@ router.post("/ingest", async (req, res) => { try { // TODO: Add basic auth/API key validation before production roll-out. const original = typeof req.body?.original === "string" ? req.body.original.trim() : ""; - const sourceLang = normalizeLang(req.body?.sourceLang || "original"); - const translations = normalizeTranslations(req.body?.translations); + const mapFromNested = normalizeTranslations(req.body?.translations); + const mapFromFlat = buildTranslationsFromFlatPayload(req.body); + const translations = { ...mapFromNested, ...mapFromFlat }; + const sourceLang = normalizeLang(req.body?.sourceLang || inferSourceLangFromTranslations(original, translations)); if (!original) { return res.status(400).json({ status: "Original text is required" }); } + if (sourceLang && sourceLang !== "original" && !translations[sourceLang]) { + translations[sourceLang] = original; + } const sequence = liveCaptionState.latestSequence + 1; const caption = { sequence, createdAt: new Date().toISOString(), - sourceLang, original, - translations, + ...translations, }; liveCaptionState.latestSequence = sequence; diff --git a/scripts/liveCaptionsTestSender.js b/scripts/liveCaptionsTestSender.js index c6178b6..80c7633 100644 --- a/scripts/liveCaptionsTestSender.js +++ b/scripts/liveCaptionsTestSender.js @@ -10,35 +10,27 @@ const intervalMs = 5000; const samples = [ { original: "Bienvenidos a nuestro servicio de adoracion.", - sourceLang: "es", - translations: { - en: "Welcome to our worship service.", - fr: "Bienvenue a notre service de louange.", - }, + es: "Bienvenidos a nuestro servicio de adoracion.", + en: "Welcome to our worship service.", + fr: "Bienvenue a notre service de louange.", }, { original: "Leamos juntos en el Salmo 23.", - sourceLang: "es", - translations: { - en: "Let us read together in Psalm 23.", - fr: "Lisons ensemble le Psaume 23.", - }, + es: "Leamos juntos en el Salmo 23.", + en: "Let us read together in Psalm 23.", + fr: "Lisons ensemble le Psaume 23.", }, { original: "Dios es fiel en todo tiempo.", - sourceLang: "es", - translations: { - en: "God is faithful at all times.", - fr: "Dieu est fidele en tout temps.", - }, + es: "Dios es fiel en todo tiempo.", + en: "God is faithful at all times.", + fr: "Dieu est fidele en tout temps.", }, { original: "Tomemos un momento para orar.", - sourceLang: "es", - translations: { - en: "Let us take a moment to pray.", - fr: "Prenons un moment pour prier.", - }, + es: "Tomemos un momento para orar.", + en: "Let us take a moment to pray.", + fr: "Prenons un moment pour prier.", }, ];