| """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) |
|
|
| 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 |
| 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()) |
|
|