#!/usr/bin/env bash # Unattended overnight pipeline: for each of the 7 Physics chapters, wait for/run # AI4Bharat narration, rebuild the production package, render the final slide-driven # MP4, and log pass/fail per stage. Never aborts the whole run on one chapter's # failure — logs it and moves to the next chapter so a bad chapter doesn't cost # the rest of the night. set -u cd "C:\Users\USER\OneDrive\Desktop\STARTUP" || exit 1 PYBIN="/c/Users/USER/AppData/Local/Microsoft/WindowsApps/PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0/python.exe" STATUS_LOG="outputs/video/physics/overnight-run-status.log" mkdir -p outputs/video/physics echo "=== overnight run started $(date) ===" >> "$STATUS_LOG" CHAPTERS="phy-p1-c1 phy-p1-c2 phy-p1-c3 phy-p1-c4 phy-p1-c5 phy-p1-c6 phy-p1-c7" manifest_complete() { local chapter_id="$1" local manifest="outputs/video/tuition/${chapter_id}/audio/ai4bharat-full-chapter/manifest.json" [ -f "$manifest" ] || return 1 python3 -c " import json, sys d = json.load(open('$manifest', encoding='utf-8')) total = d.get('naturalPhraseCount') or 0 # Two manifest shapes exist: in-progress (has completedPhraseCount) and the # final post-loop write (no completedPhraseCount, but every chunk present # and marked synthesized). Recognize both as complete. done = d.get('completedPhraseCount') if done is None: chunks = d.get('chunks') or [] done = sum(1 for c in chunks if c.get('status') == 'synthesized') sys.exit(0 if total and done == total else 1) " 2>/dev/null } for ch in $CHAPTERS; do echo "--- $ch: checking narration ---" >> "$STATUS_LOG" if manifest_complete "$ch"; then echo "[$ch] AI4Bharat narration already complete" >> "$STATUS_LOG" else # generate_ai4bharat_chapter_audio.py resumes from cached completed # phrases on its own (checks the existing manifest), so it's always safe # to just (re)invoke it here — including after a crash/power loss. echo "[$ch] generating AI4Bharat narration (resuming if partially done)..." >> "$STATUS_LOG" "$PYBIN" backend/scripts/generate_ai4bharat_chapter_audio.py --chapter-id "$ch" >> "outputs/video/physics/${ch}-audio.log" 2>&1 if manifest_complete "$ch"; then echo "[$ch] narration generation succeeded" >> "$STATUS_LOG" else echo "[$ch] FAILED narration generation — see ${ch}-audio.log — skipping to next chapter" >> "$STATUS_LOG" continue fi fi echo "[$ch] rebuilding production package..." >> "$STATUS_LOG" if CHAPTER_ID="$ch" npx tsx scripts/video-engine/build-physics-production-packages.ts >> "outputs/video/physics/${ch}-build.log" 2>&1; then echo "[$ch] package build OK" >> "$STATUS_LOG" else echo "[$ch] FAILED package build — see ${ch}-build.log — skipping render" >> "$STATUS_LOG" continue fi echo "[$ch] rendering final video..." >> "$STATUS_LOG" if CHAPTER_ID="$ch" npx tsx scripts/video-engine/render-physics-slide-chapters.ts >> "outputs/video/physics/${ch}-render.log" 2>&1; then echo "[$ch] RENDER OK" >> "$STATUS_LOG" else echo "[$ch] FAILED render — see ${ch}-render.log" >> "$STATUS_LOG" fi done echo "=== all 7 English chapters attempted $(date) ===" >> "$STATUS_LOG" # Malayalam: short proof-of-concept only (opening + first concept phrases of # Sound Waves), NOT a full chapter or a rendered video. Translation quality # for a science class needs human review before it goes in front of a # student, so this produces a reviewable script + audio sample, not a # finished asset. Doing this AFTER all 7 English chapters so it never delays # the primary, explicit ask. echo "--- Malayalam proof-of-concept (Sound Waves opening) ---" >> "$STATUS_LOG" "$PYBIN" backend/scripts/generate_malayalam_proof_sample.py >> "outputs/video/physics/malayalam-proof.log" 2>&1 if [ $? -eq 0 ]; then echo "[malayalam-proof] OK — needs human translation review before real use" >> "$STATUS_LOG" else echo "[malayalam-proof] FAILED — see malayalam-proof.log" >> "$STATUS_LOG" fi echo "=== overnight run finished $(date) ===" >> "$STATUS_LOG"