From d22b524182b1e6e42be50916d9987392c8aa6fa6 Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Sat, 21 Feb 2026 11:21:51 +0100 Subject: [PATCH] fix: create parent directories for targets in subfolders Build targets with subfolder paths (e.g. img/file.jpg) failed because parent directories did not exist. Create them on demand in _build_single_target before dispatching to providers. Also clean up empty subdirectories in the clean command after removing target files. --- hokusai/builder.py | 3 +++ hokusai/cli.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/hokusai/builder.py b/hokusai/builder.py index c085da8..08774b5 100644 --- a/hokusai/builder.py +++ b/hokusai/builder.py @@ -145,6 +145,9 @@ async def _build_single_target( provider_index: dict[str, Provider], ) -> None: """Build a single target by dispatching to the appropriate provider.""" + # Ensure parent directories exist for targets in subfolders. + (project_dir / target_name).parent.mkdir(parents=True, exist_ok=True) + target_cfg = config.targets[target_name] if isinstance(target_cfg, DownloadTargetConfig): diff --git a/hokusai/cli.py b/hokusai/cli.py index f58ad1b..df96bc5 100644 --- a/hokusai/cli.py +++ b/hokusai/cli.py @@ -194,12 +194,26 @@ def clean() -> None: state_name = state_filename(_project_name(config_path)) removed = 0 + dirs_to_check: set[Path] = set() for target_name in config.targets: target_path = project_dir / target_name if target_path.exists(): target_path.unlink() click.echo(click.style(" rm ", fg="red") + target_name) removed += 1 + # Track parent dirs that may now be empty. + parent = target_path.parent + if parent != project_dir: + dirs_to_check.add(parent) + + # Remove empty subdirectories left behind, bottom-up. + for d in sorted(dirs_to_check, key=lambda p: len(p.parts), reverse=True): + while d != project_dir and d.exists() and not any(d.iterdir()): + d.rmdir() + click.echo( + click.style(" rm ", fg="red") + str(d.relative_to(project_dir)) + "/" + ) + d = d.parent state_path = project_dir / state_name if state_path.exists():