"""Malayalam proof-of-concept for the Sound Waves opening — NOT a finished asset. Translates the first few teaching phrases to Malayalam (Groq) and synthesizes them with AI4Bharat's Anjali voice, so there is something concrete to review before committing GPU-hours to a full Malayalam chapter. Machine-translated science vocabulary needs a human check before it reaches a real class. """ from __future__ import annotations import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "backend")) sys.path.insert(0, str(ROOT / "backend" / "scripts")) import importlib.util kokoro_spec = importlib.util.spec_from_file_location("kokoro_chapter_audio", ROOT / "backend/scripts/generate_kokoro_chapter_audio.py") kokoro_mod = importlib.util.module_from_spec(kokoro_spec) kokoro_spec.loader.exec_module(kokoro_mod) blueprint_spec = importlib.util.spec_from_file_location("blueprints", ROOT / "backend/scripts/generate_source_backed_science_blueprints.py") blueprint_mod = importlib.util.module_from_spec(blueprint_spec) blueprint_spec.loader.exec_module(blueprint_mod) PHRASE_LIMIT = 6 OUTPUT_DIR = ROOT / "outputs" / "video" / "physics" / "malayalam-proof" def translate_to_malayalam(generate_groq, text: str) -> str: prompt = f"""Translate this Kerala SSLC Class 10 Physics teacher's sentence into natural spoken Malayalam for text-to-speech. Rules: - Keep scientific terms Kerala students actually learn in English embedded as-is (e.g. compression, rarefaction, vibration, wavelength, frequency, longitudinal wave) — do not translate or transliterate them, just leave the English word inside the Malayalam sentence, matching how Malayalam-medium physics teachers really code-mix in class. - Natural spoken rhythm, not a stiff word-for-word translation. - Return ONLY the Malayalam sentence as a JSON object: {{"malayalam": "..."}} English sentence: {text} """ result = generate_groq(prompt) return result["malayalam"] def main() -> int: groq_key = blueprint_mod.env_value("GROQ_API_KEY") model = "meta-llama/llama-4-scout-17b-16e-instruct" generate_groq = lambda prompt: blueprint_mod.call_groq(prompt, groq_key, model) # noqa: E731 source_manifest_path = ROOT / "outputs/video/tuition/phy-p1-c1/audio/full-chapter/manifest.json" source_manifest = json.loads(source_manifest_path.read_text(encoding="utf-8")) merged = kokoro_mod.merge_scene_chunks(source_manifest["chunks"])[:PHRASE_LIMIT] import torch # noqa: F401 - fail fast with a clear error if the wrong interpreter is used from app.schemas.video import VideoSceneAudioInput from app.services.tts_provider import AI4BharatIndicParlerTTSProvider provider = AI4BharatIndicParlerTTSProvider() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) review_entries = [] for index, phrase in enumerate(merged, start=1): malayalam_text = translate_to_malayalam(generate_groq, phrase["spokenText"]) scene = VideoSceneAudioInput(scene_id=index, type="concept", duration_seconds=max(2.0, len(malayalam_text.split()) / 1.8), voice_text=malayalam_text) output_path = OUTPUT_DIR / f"{index:02d}-{phrase['sceneId']}.wav" result = provider.generate_scene_audio(scene=scene, output_file=output_path, voice_mode="malayalam_soft", voice="Anjali", language="ml") review_entries.append( { "index": index, "sceneId": phrase["sceneId"], "english": phrase["spokenText"], "malayalam_machine_translation": malayalam_text, "audioFile": str(output_path.relative_to(ROOT)).replace("\\", "/"), "durationSeconds": result.duration_seconds, } ) print(f"[{index}/{len(merged)}] {phrase['sceneId']} synthesized", flush=True) manifest = { "warning": "MACHINE TRANSLATION, NOT HUMAN-REVIEWED. Verify every Malayalam line against actual Kerala SSLC physics terminology before using this in a real class video.", "chapterId": "phy-p1-c1", "voice": "Anjali", "phraseCount": len(review_entries), "entries": review_entries, } (OUTPUT_DIR / "review-manifest.json").write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") print(f"Wrote {OUTPUT_DIR / 'review-manifest.json'}", flush=True) return 0 if __name__ == "__main__": sys.exit(main())