Files
test2/scripts/modules/blend_render_info.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

124 lines
3.7 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2010-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# This module can get render info without running from inside blender.
__all__ = (
"read_blend_rend_chunk",
)
import _blendfile_header
class RawBlendFileReader:
"""
Return a file handle to the raw blend file data (abstracting compressed formats).
"""
__slots__ = (
# The path to load.
"_filepath",
# The file base file handler or None (only set for compressed formats).
"_blendfile_base",
# The file handler to return to the caller (always uncompressed data).
"_blendfile",
)
def __init__(self, filepath):
self._filepath = filepath
self._blendfile_base = None
self._blendfile = None
def __enter__(self):
blendfile = open(self._filepath, "rb")
blendfile_base = None
head = blendfile.read(4)
blendfile.seek(0)
if head[0:2] == b'\x1f\x8b': # GZIP magic.
import gzip
blendfile_base = blendfile
blendfile = gzip.open(blendfile, "rb")
elif head[0:4] == b'\x28\xb5\x2f\xfd': # Z-standard magic.
import zstandard
blendfile_base = blendfile
blendfile = zstandard.open(blendfile, "rb")
self._blendfile_base = blendfile_base
self._blendfile = blendfile
return self._blendfile
def __exit__(self, _exc_type, _exc_value, _exc_traceback):
self._blendfile.close()
if self._blendfile_base is not None:
self._blendfile_base.close()
return False
def get_render_info_structure(endian_str, size):
import struct
# The maximum size of the scene name changed over time, so create a different
# structure depending on the size of the entire block.
if size == 2 * 4 + 24:
return struct.Struct(endian_str + b'ii24s')
if size == 2 * 4 + 64:
return struct.Struct(endian_str + b'ii64s')
if size == 2 * 4 + 256:
return struct.Struct(endian_str + b'ii256s')
raise ValueError("Unknown REND chunk size: {:d}".format(size))
def _read_blend_rend_chunk_from_file(blendfile, filepath):
import sys
from os import SEEK_CUR
try:
blender_header = _blendfile_header.BlendFileHeader(blendfile)
except _blendfile_header.BlendHeaderError:
sys.stderr.write("Not a blend file: {:s}\n".format(filepath))
return []
scenes = []
endian_str = b'<' if blender_header.is_little_endian else b'>'
block_header_struct = blender_header.create_block_header_struct()
while bhead := _blendfile_header.BlockHeader(blendfile, block_header_struct):
if bhead.code == b'ENDB':
break
remaining_bytes = bhead.size
if bhead.code == b'REND':
rend_block_struct = get_render_info_structure(endian_str, bhead.size)
start_frame, end_frame, scene_name = rend_block_struct.unpack(blendfile.read(rend_block_struct.size))
remaining_bytes -= rend_block_struct.size
scene_name = scene_name[:scene_name.index(b'\0')]
# It's possible old blend files are not UTF8 compliant, use `surrogateescape`.
scene_name = scene_name.decode("utf8", errors="surrogateescape")
scenes.append((start_frame, end_frame, scene_name))
blendfile.seek(remaining_bytes, SEEK_CUR)
return scenes
def read_blend_rend_chunk(filepath):
with RawBlendFileReader(filepath) as blendfile:
return _read_blend_rend_chunk_from_file(blendfile, filepath)
def main():
import sys
for filepath in sys.argv[1:]:
for value in read_blend_rend_chunk(filepath):
print("{:d} {:d} {:s}".format(*value))
2018-07-03 06:27:53 +02:00
if __name__ == '__main__':
main()