예제 #1
0
파일: test_pack.py 프로젝트: GigiSpligi/bat
    def test_compression(self):
        blendname = 'subdir/doubly_linked_up.blend'
        imgfile = self.blendfiles / blendname

        packer = pack.Packer(imgfile,
                             self.blendfiles,
                             self.tpath,
                             compress=True)
        packer.strategise()
        packer.execute()

        dest = self.tpath / blendname
        self.assertTrue(dest.exists())
        self.assertTrue(blendfile.open_cached(dest).is_compressed)

        for bpath in self.tpath.rglob('*.blend'):
            if bpath == dest:
                # Only test files that were bundled as dependency; the main
                # file was tested above already.
                continue
            self.assertTrue(
                blendfile.open_cached(bpath).is_compressed,
                'Expected %s to be compressed' % bpath)
            break
        else:
            self.fail('Expected to have Blend files in the BAT pack.')

        for imgpath in self.tpath.rglob('*.jpg'):
            with imgpath.open('rb') as imgfile:
                magic = imgfile.read(3)
            self.assertEqual(b'\xFF\xD8\xFF', magic,
                             'Expected %s to NOT be compressed' % imgpath)
            break
        else:
            self.fail('Expected to have JPEG files in the BAT pack.')
예제 #2
0
    def test_compressed(self):
        infile = self.blendfiles / 'linked_cube_compressed.blend'
        bf1 = blendfile.open_cached(infile)
        bf2 = blendfile.open_cached(infile)

        # The file should only be opened & parsed once.
        self.assertIs(bf1, bf2)
        self.assertIs(bf1, blendfile._cached_bfiles[infile])
예제 #3
0
    def test_open_cached(self):
        infile = self.blendfiles / 'basic_file.blend'
        bf1 = blendfile.open_cached(infile)
        bf2 = blendfile.open_cached(infile)

        # The file should only be opened & parsed once.
        self.assertIs(bf1, bf2)
        self.assertIs(bf1, blendfile._cached_bfiles[infile])
예제 #4
0
    def test_close_one_file(self):
        path1 = self.blendfiles / 'linked_cube_compressed.blend'
        path2 = self.blendfiles / 'basic_file.blend'
        bf1 = blendfile.open_cached(path1)
        bf2 = blendfile.open_cached(path2)
        self.assertIs(bf1, blendfile._cached_bfiles[path1])

        # Closing a file should remove it from the cache.
        bf1.close()
        self.assertTrue(bf1.fileobj.closed)
        self.assertEqual({path2: bf2}, blendfile._cached_bfiles)
예제 #5
0
def deps(bfilepath: pathlib.Path, progress_cb: typing.Optional[progress.Callback] = None) \
        -> typing.Iterator[result.BlockUsage]:
    """Open the blend file and report its dependencies.

    :param bfilepath: File to open.
    :param progress_cb: Progress callback object.
    """

    log.info('opening: %s', bfilepath)
    bfile = blendfile.open_cached(bfilepath)

    bi = file2blocks.BlockIterator()
    if progress_cb:
        bi.progress_cb = progress_cb

    # Remember which block usages we've reported already, without keeping the
    # blocks themselves in memory.
    seen_hashes = set()  # type: typing.Set[int]

    for block in asset_holding_blocks(bi.iter_blocks(bfile)):
        for block_usage in blocks2assets.iter_assets(block):
            usage_hash = hash(block_usage)
            if usage_hash in seen_hashes:
                continue
            seen_hashes.add(usage_hash)
            yield block_usage
예제 #6
0
    def _open_and_rebind_test(self, infile: pathlib.Path, other: pathlib.Path):
        self.assertFalse(other.exists())

        bf = blendfile.open_cached(infile)

        self.assertEqual(str(bf.raw_filepath), bf.fileobj.name)

        before_filepath = bf.filepath
        before_raw_fp = bf.raw_filepath
        before_blocks = bf.blocks
        before_compressed = bf.is_compressed

        bf.copy_and_rebind(other, mode='rb+')

        self.assertTrue(other.exists())
        self.assertEqual(before_compressed, bf.is_compressed)

        if bf.is_compressed:
            self.assertNotEqual(bf.filepath, bf.raw_filepath)
        else:
            self.assertEqual(bf.filepath, bf.raw_filepath)

        self.assertNotEqual(before_filepath, bf.filepath)
        self.assertNotEqual(before_raw_fp, bf.raw_filepath)
        self.assertEqual(other, bf.filepath)
        self.assertIs(before_blocks, bf.blocks)
        self.assertNotIn(infile, blendfile._cached_bfiles)
        self.assertIs(bf, blendfile._cached_bfiles[other])

        self.assertEqual(str(bf.raw_filepath), bf.fileobj.name)
예제 #7
0
    def test_closed(self):
        infile = self.blendfiles / 'linked_cube_compressed.blend'
        bf = blendfile.open_cached(infile)
        self.assertIs(bf, blendfile._cached_bfiles[infile])

        blendfile.close_all_cached()
        self.assertTrue(bf.fileobj.closed)
        self.assertEqual({}, blendfile._cached_bfiles)
예제 #8
0
파일: test_pack.py 프로젝트: GigiSpligi/bat
    def test_execute_rewrite_no_touch_origs(self):
        infile, _ = self._pack_with_rewrite()

        # The original file shouldn't be touched.
        bfile = blendfile.open_cached(infile, assert_cached=False)
        libs = sorted(bfile.code_index[b'LI'])

        self.assertEqual(b'LILib', libs[0].id_name)
        self.assertEqual(b'//../linked_cube.blend', libs[0][b'name'])
        self.assertEqual(b'LILib.002', libs[1].id_name)
        self.assertEqual(b'//../material_textures.blend', libs[1][b'name'])
예제 #9
0
    def _visit_linked_blocks(self, blocks_per_lib):
        # We've gone through all the blocks in this file, now open the libraries
        # and iterate over the blocks referred there.
        for lib_bpath, idblocks in blocks_per_lib.items():
            lib_path = pathlib.Path(lib_bpath.to_path())
            try:
                lib_path = lib_path.resolve()
            except FileNotFoundError:
                log.warning('Library %s does not exist', lib_path)
                continue

            log.debug('Expanding %d blocks in %s', len(idblocks), lib_path)
            libfile = blendfile.open_cached(lib_path)
            yield from self.iter_blocks(libfile, idblocks)
예제 #10
0
    def _visit_linked_blocks(self, blocks_per_lib):
        # We've gone through all the blocks in this file, now open the libraries
        # and iterate over the blocks referred there.
        for lib_bpath, idblocks in blocks_per_lib.items():
            lib_path = bpathlib.make_absolute(lib_bpath.to_path())

            assert lib_path.exists()
            if not lib_path.exists():
                log.warning('Library %s does not exist', lib_path)
                continue

            log.debug('Expanding %d blocks in %s', len(idblocks), lib_path)
            libfile = blendfile.open_cached(lib_path)
            yield from self.iter_blocks(libfile, idblocks)
예제 #11
0
파일: test_pack.py 프로젝트: GigiSpligi/bat
    def test_execute_rewrite(self):
        infile, _ = self._pack_with_rewrite()

        extpath = pathlib.PurePosixPath('//_outside_project',
                                        *self.blendfiles.parts[1:])
        extbpath = bpathlib.BlendPath(extpath)

        # Those libraries should be properly rewritten.
        bfile = blendfile.open_cached(self.tpath / infile.name,
                                      assert_cached=False)
        libs = sorted(bfile.code_index[b'LI'])
        self.assertEqual(b'LILib', libs[0].id_name)
        self.assertEqual(extbpath / b'linked_cube.blend', libs[0][b'name'])
        self.assertEqual(b'LILib.002', libs[1].id_name)
        self.assertEqual(extbpath / b'material_textures.blend',
                         libs[1][b'name'])
예제 #12
0
파일: test_pack.py 프로젝트: GigiSpligi/bat
    def test_noop(self):
        ppath = self.blendfiles / 'subdir'
        infile = ppath / 'doubly_linked_up.blend'

        packer = pack.Packer(infile, ppath, self.tpath, noop=True)
        packer.strategise()
        packer.execute()

        self.assertEqual([], list(self.tpath.iterdir()))

        # The original file shouldn't be touched.
        bfile = blendfile.open_cached(infile)
        libs = sorted(bfile.code_index[b'LI'])

        self.assertEqual(b'LILib', libs[0].id_name)
        self.assertEqual(b'//../linked_cube.blend', libs[0][b'name'])
        self.assertEqual(b'LILib.002', libs[1].id_name)
        self.assertEqual(b'//../material_textures.blend', libs[1][b'name'])
예제 #13
0
파일: test_pack.py 프로젝트: GigiSpligi/bat
    def test_rewrite_sequence(self):
        ppath = self.blendfiles / 'subdir'
        infile = ppath / 'image_sequence_dir_up.blend'

        with pack.Packer(infile, ppath, self.tpath) as packer:
            packer.strategise()
            packer.execute()

        bf = blendfile.open_cached(self.tpath / infile.name,
                                   assert_cached=False)
        scene = bf.code_index[b'SC'][0]
        ed = scene.get_pointer(b'ed')
        seq = ed.get_pointer((b'seqbase', b'first'))
        seq_strip = seq.get_pointer(b'strip')

        imgseq_path = (self.blendfiles / 'imgseq').absolute()
        as_bytes = str(imgseq_path.relative_to(imgseq_path.anchor)).encode()
        relpath = bpathlib.BlendPath(b'//_outside_project') / as_bytes

        # The image sequence base path should be rewritten.
        self.assertEqual(b'SQ000210.png', seq[b'name'])
        self.assertEqual(relpath, seq_strip[b'dir'])
예제 #14
0
    def _rewrite_paths(self) -> None:
        """Rewrite paths to the new location of the assets.

        Writes the rewritten blend files to a temporary location.
        """

        for bfile_path, action in self._actions.items():
            if not action.rewrites:
                continue
            self._check_aborted()

            assert isinstance(bfile_path, pathlib.Path)
            # bfile_pp is the final path of this blend file in the BAT pack.
            # It is used to determine relative paths to other blend files.
            # It is *not* used for any disk I/O, since the file may not even
            # exist on the local filesystem.
            bfile_pp = action.new_path
            assert bfile_pp is not None

            # Use tempfile to create a unique name in our temporary directoy.
            # The file should be deleted when self.close() is called, and not
            # when the bfile_tp object is GC'd.
            bfile_tmp = tempfile.NamedTemporaryFile(dir=str(self._rewrite_in),
                                                    prefix='bat-',
                                                    suffix='-' +
                                                    bfile_path.name,
                                                    delete=False)
            bfile_tp = pathlib.Path(bfile_tmp.name)
            action.read_from = bfile_tp
            log.info('Rewriting %s to %s', bfile_path, bfile_tp)

            # The original blend file will have been cached, so we can use it
            # to avoid re-parsing all data blocks in the to-be-rewritten file.
            bfile = blendfile.open_cached(bfile_path, assert_cached=True)
            bfile.copy_and_rebind(bfile_tp, mode='rb+')

            for usage in action.rewrites:
                self._check_aborted()
                assert isinstance(usage, result.BlockUsage)
                asset_pp = self._actions[usage.abspath].new_path
                assert isinstance(asset_pp, pathlib.Path)

                log.debug('   - %s is packed at %s', usage.asset_path,
                          asset_pp)
                relpath = bpathlib.BlendPath.mkrelative(asset_pp, bfile_pp)
                if relpath == usage.asset_path:
                    log.info('   - %s remained at %s', usage.asset_path,
                             relpath)
                    continue

                log.info('   - %s moved to %s', usage.asset_path, relpath)

                # Find the same block in the newly copied file.
                block = bfile.dereference_pointer(usage.block.addr_old)
                if usage.path_full_field is None:
                    dir_field = usage.path_dir_field
                    assert dir_field is not None
                    log.debug('   - updating field %s of block %s',
                              dir_field.name.name_only, block)
                    reldir = bpathlib.BlendPath.mkrelative(
                        asset_pp.parent, bfile_pp)
                    written = block.set(dir_field.name.name_only, reldir)
                    log.debug('   - written %d bytes', written)

                    # BIG FAT ASSUMPTION that the filename (e.g. basename
                    # without path) does not change. This makes things much
                    # easier, as in the sequence editor the directory and
                    # filename fields are in different blocks. See the
                    # blocks2assets.scene() function for the implementation.
                else:
                    log.debug('   - updating field %s of block %s',
                              usage.path_full_field.name.name_only, block)
                    written = block.set(usage.path_full_field.name.name_only,
                                        relpath)
                    log.debug('   - written %d bytes', written)

            # Make sure we close the file, otherwise changes may not be
            # flushed before it gets copied.
            if bfile.is_modified:
                self._progress_cb.rewrite_blendfile(bfile_path)
            bfile.close()
## list items in blend using blender asset tracer
from pathlib import Path
from blender_asset_tracer import blendfile
'''
Brush = BR
Object = OB
WindowManager = WM
WindowSpace = WS
Screen = SR
Collection = GR
NodeTree = NT
Palette = PL
Image = IM
Action = AC
Library = LI
Material = MA
Text = TX
Mesh = ME
'''

fp = Path('path/to/file')
print(f'Items in {str(fp)}:\n')
with blendfile.open_cached(fp) as bf:
    for item in bf.find_blocks_from_code(b'OB'):
        print(item.id_name.decode()[2:])

print('\nDone')