#!/usr/bin/env python3 import hashlib import json import pathlib import sys import bpy LIBRARY_NAME = "WBV2P3005Library.blend" LINKED_OBJECT = "WBV2P3005LinkedObject" APPEND_OBJECT = "WBV2P3005AppendObject" def report_state() -> dict: libraries = list(bpy.data.libraries) if len(libraries) != 1: raise RuntimeError(f"expected one external library, found {len(libraries)}") library = libraries[0] linked = bpy.data.objects.get(LINKED_OBJECT) if linked is None or linked.library is None: raise RuntimeError("linked object ownership was not retained") return { "library": { "name": library.name, "sourcePath": library.filepath, "packed": library.packed_file is not None, "readOnly": True, "dependencyIds": [], }, "linked": { "name": linked.name, "library": linked.library.name, "readOnly": linked.library is not None, "override": linked.override_library is not None, }, } def source_state(source: pathlib.Path) -> dict: bpy.ops.wm.open_mainfile(filepath=str(source), load_ui=False) appended = bpy.data.objects.get(APPEND_OBJECT) if appended is None or appended.data is None or len(appended.data.materials) != 1: raise RuntimeError("append source closure is incomplete") material = appended.data.materials[0] image = next((node.image for node in material.node_tree.nodes if node.type == "TEX_IMAGE" and node.image), None) if image is None or image.packed_file is None: raise RuntimeError("append source image is not packed") return { "object": appended.name, "mesh": appended.data.name, "material": material.name, "image": image.name, "imagePacked": True, } def main() -> None: arguments = sys.argv[sys.argv.index("--") + 1:] if len(arguments) != 3: raise SystemExit("usage: blender -b --python check-wbv2-p3-005-desktop.py -- FIXTURE SOURCE REPORT") fixture, source, output = (pathlib.Path(value).resolve() for value in arguments) bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False) before = report_state() source_closure = source_state(source) bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False) reopen_before = report_state() if before != reopen_before: raise RuntimeError(f"fixture reload drift: {before} != {reopen_before}") temporary = fixture.with_name("wbv2-p3-005-reopen.blend") try: bpy.ops.wm.save_as_mainfile(filepath=str(temporary), check_existing=False, compress=True) bpy.ops.wm.open_mainfile(filepath=str(temporary), load_ui=False) after = report_state() finally: temporary.unlink(missing_ok=True) if before != after: raise RuntimeError(f"library save/reopen drift: {before} != {after}") report = { "schemaVersion": 1, "task": "WBV2-P3-005", "operation": "ASSET_LIBRARY_OWNERSHIP_DESKTOP", "fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(), "sourceSha256": hashlib.sha256(source.read_bytes()).hexdigest(), "sourceClosure": source_closure, "state": after, "saveReopen": "EXACT", "blenderVersion": bpy.app.version_string, } output.parent.mkdir(parents=True, exist_ok=True) output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") print(f"wbv2-p3-005-desktop-ok library={after['library']['name']} linked={after['linked']['name']} appendImage=packed saveReopen=exact") if __name__ == "__main__": main()