From 606377180cd1a3e6e1bf4621a38010966527808b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 2 Nov 2023 14:53:52 +0100 Subject: [PATCH] Pack: always pack files, explode directories to a list of files When an asset is represented as directory in Blender (for example fluid simulation caches), that directory is traced and each file is considered an asset. This makes it considerably easier for Shaman clients, as they need to compute the SHA256 checksum of each file. The logic to transform a directory path to a list of the contained files is now in BAT itself. --- blender_asset_tracer/pack/__init__.py | 27 ++++++++++++++++++--- blender_asset_tracer/pack/transfer.py | 4 +++ blender_asset_tracer/trace/file_sequence.py | 5 +++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/blender_asset_tracer/pack/__init__.py b/blender_asset_tracer/pack/__init__.py index f3ac76c..59ace62 100644 --- a/blender_asset_tracer/pack/__init__.py +++ b/blender_asset_tracer/pack/__init__.py @@ -554,9 +554,15 @@ class Packer: bfile.close() def _copy_asset_and_deps(self, asset_path: pathlib.Path, action: AssetAction): + asset_path_is_dir = asset_path.is_dir() + # Copy the asset itself, but only if it's not a sequence (sequences are # handled below in the for-loop). - if "*" not in str(asset_path) and "" not in asset_path.name: + if ( + "*" not in str(asset_path) + and "" not in asset_path.name + and not asset_path_is_dir + ): packed_path = action.new_path assert packed_path is not None read_path = action.read_from or asset_path @@ -564,6 +570,11 @@ class Packer: read_path, packed_path, may_move=action.read_from is not None ) + if asset_path_is_dir: # like 'some/directory': + asset_base_path = asset_path + else: # like 'some/directory/prefix_*.bphys': + asset_base_path = asset_path.parent + # Copy its sequence dependencies. for usage in action.usages: if not usage.is_sequence: @@ -571,14 +582,24 @@ class Packer: first_pp = self._actions[usage.abspath].new_path assert first_pp is not None + log.info(f"first_pp = {first_pp}") # In case of globbing, we only support globbing by filename, # and not by directory. assert "*" not in str(first_pp) or "*" in first_pp.name - packed_base_dir = first_pp.parent + if asset_path_is_dir: + packed_base_dir = first_pp + else: + packed_base_dir = first_pp.parent + for file_path in usage.files(): - packed_path = packed_base_dir / file_path.name + # Compute the relative path, to support cases where asset_path + # is `some/directory` and the to-be-copied file is in + # `some/directory/subdir/filename.txt`. + relpath = file_path.relative_to(asset_base_path) + packed_path = packed_base_dir / relpath + # Assumption: assets in a sequence are never blend files. self._send_to_target(file_path, packed_path) diff --git a/blender_asset_tracer/pack/transfer.py b/blender_asset_tracer/pack/transfer.py index b4e543c..62b1b1c 100644 --- a/blender_asset_tracer/pack/transfer.py +++ b/blender_asset_tracer/pack/transfer.py @@ -90,6 +90,8 @@ class FileTransferer(threading.Thread, metaclass=abc.ABCMeta): def queue_copy(self, src: pathlib.Path, dst: pathlib.PurePath): """Queue a copy action from 'src' to 'dst'.""" + if src.is_dir(): + raise TypeError(f"only files can be copied, not directories: {src}") assert ( not self.done.is_set() ), "Queueing not allowed after done_and_join() was called" @@ -103,6 +105,8 @@ class FileTransferer(threading.Thread, metaclass=abc.ABCMeta): def queue_move(self, src: pathlib.Path, dst: pathlib.PurePath): """Queue a move action from 'src' to 'dst'.""" + if src.is_dir(): + raise TypeError(f"only files can be moved, not directories: {src}") assert ( not self.done.is_set() ), "Queueing not allowed after done_and_join() was called" diff --git a/blender_asset_tracer/trace/file_sequence.py b/blender_asset_tracer/trace/file_sequence.py index f5d4344..6a28567 100644 --- a/blender_asset_tracer/trace/file_sequence.py +++ b/blender_asset_tracer/trace/file_sequence.py @@ -56,7 +56,10 @@ def expand_sequence(path: pathlib.Path) -> typing.Iterator[pathlib.Path]: raise DoesNotExist(path) if path.is_dir(): - yield path + # Explode directory paths into separate files. + for subpath in path.rglob("*"): + if subpath.is_file(): + yield subpath return log.debug("expanding file sequence %s", path)