File size: 4,465 Bytes
7c6ffa6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | """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())
|