ConsistentlyInconsistentYT-.../blenderrestarteraftercrashing.py
ConsistentlyInconsistentYT 011722ac4e
Create blenderrestarteraftercrashing.py
Rip Princess Diana
2025-11-06 21:22:29 +13:00

93 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Watch-dog that restarts Blender until every entry in metadata.json
is "rendered": true. Now hardened against path-quoting issues and
third-party add-ons crashing on start-up.
"""
import json, os, subprocess, sys, tempfile, time
from pathlib import Path
import textwrap, json as _json # for safe string literal
# ─────────────────────────── USER SETTINGS ────────────────────────────
BLENDER_EXE = r"C:\Program Files\Blender Foundation\Blender 3.6\blender.exe"
BLEND_FILE = r"C:\Users\youruser\Desktop\birdge.blend"
JOB_SCRIPT = r"C:\Users\youruser\Desktop\birdge.py"
OUTPUT_DIR = r"C:\Users\Rhoady\Desktop\pet projects\relcams40"
META_PATH = Path(OUTPUT_DIR) / "metadata.json"
CRASH_SLEEP = 5.0 # seconds between retries
# ───────────────────────────────────────────────────────────────────────
# ----------------------------------------------------------------------
def all_frames_done(meta_file: Path) -> bool:
if not meta_file.exists():
return False
with meta_file.open("r") as fh:
return all(m.get("rendered") for m in json.load(fh))
# ----------------------------------------------------------------------
def make_wrapper() -> Path:
wrapper = Path(tempfile.gettempdir()) / "run_render_job_once.py"
safe_job = _json.dumps(JOB_SCRIPT) # proper quoting
safe_json = _json.dumps(str(META_PATH)) # ← new
wrapper.write_text(textwrap.dedent(f"""
import importlib.util, types, pathlib, sys, os
# ---- import the render job script ----------------------------------
job_path = pathlib.Path({safe_job})
spec = importlib.util.spec_from_file_location("job", job_path)
if spec and spec.loader:
job = importlib.util.module_from_spec(spec)
spec.loader.exec_module(job)
else: # very old Python fallback
job = types.ModuleType("job")
exec(job_path.read_text(), job.__dict__)
sys.modules["job"] = job
# ---- pick fresh vs. resume ----------------------------------------
meta_path = pathlib.Path({safe_json})
job.NEW_PHOTOS = not meta_path.exists() # True on first run only
print(f"[WRAPPER] NEW_PHOTOS = {{job.NEW_PHOTOS}}")
job.main()
"""))
return wrapper
# ----------------------------------------------------------------------
def launch_blender(wrapper: Path) -> int:
cmd = [
BLENDER_EXE,
"--factory-startup", # <-- disables all add-ons
"-b", BLEND_FILE,
"--python", str(wrapper)
]
return subprocess.run(cmd).returncode
# ----------------------------------------------------------------------
def main():
print("▶ Blender render watchdog started.")
wrapper = make_wrapper()
while True:
if all_frames_done(META_PATH):
print("✓ All frames already rendered nothing to do. Exiting.")
return
print("→ Launching Blender …")
rc = launch_blender(wrapper)
if rc == 0 and all_frames_done(META_PATH):
print("✓ Job completed on this pass. Exiting.")
return
print(f"⚠ Blender exited (code {rc}). Will retry in {CRASH_SLEEP}s …")
time.sleep(CRASH_SLEEP)
# ----------------------------------------------------------------------
if __name__ == "__main__":
main()