diff --git a/photo_metadata.py b/photo_metadata.py index f494558..3e50f52 100644 --- a/photo_metadata.py +++ b/photo_metadata.py @@ -378,6 +378,19 @@ def determine_working_dir(args: list[str]) -> Path: return common +def cleanup_roots_from_args(args: list[str]) -> list[Path]: + roots: list[Path] = [] + seen: set[Path] = set() + for arg in args: + path = Path(arg).expanduser() + if path.is_dir(): + resolved = path.resolve() + if resolved not in seen: + seen.add(resolved) + roots.append(resolved) + return roots + + def require_exiftool() -> str: executable = which("exiftool") if executable: @@ -1151,7 +1164,35 @@ def unique_existing_target(path: Path, planned_sources: set[Path]) -> Path: raise RuntimeError(f"Could not find available target name for {path}") -def apply_changes(executable: str, records: list[MediaRecord], choices: UserChoices) -> list[str]: +def remove_empty_dirs(cleanup_roots: list[Path], keep_dirs: set[Path]) -> list[Path]: + removed: list[Path] = [] + candidates: set[Path] = set() + for root in cleanup_roots: + if not root.exists() or not root.is_dir(): + continue + for directory, subdirs, _files in os.walk(root, topdown=False): + path = Path(directory).resolve() + candidates.add(path) + for subdir in subdirs: + child = (Path(directory) / subdir).resolve() + if child.exists() and child.is_dir(): + candidates.add(child) + + for directory in sorted(candidates, key=lambda path: len(path.parts), reverse=True): + if directory in keep_dirs: + continue + try: + directory.rmdir() + removed.append(directory) + except OSError: + pass + + return removed + + +def apply_changes( + executable: str, records: list[MediaRecord], choices: UserChoices +) -> list[str]: failures: list[str] = [] planned_sources = {record.path.resolve() for record in records} @@ -1178,6 +1219,14 @@ def apply_changes(executable: str, records: list[MediaRecord], choices: UserChoi return failures +def print_removed_dirs(removed_dirs: list[Path]) -> None: + if not removed_dirs: + return + print("\nRemoved empty director" + ("y:" if len(removed_dirs) == 1 else "ies:")) + for directory in removed_dirs: + print(f" - {directory}") + + def prepare_plan(records: list[MediaRecord], choices: UserChoices) -> list[list[MediaRecord]]: apply_time_offset(records, choices.time_offset) @@ -1213,6 +1262,7 @@ def run(argv: list[str]) -> int: print(exc) return 1 + cleanup_roots = cleanup_roots_from_args(argv[1:]) files = collect_media_files(argv[1:]) if not files: print("No supported media files found.") @@ -1252,6 +1302,9 @@ def run(argv: list[str]) -> int: return 0 failures = apply_changes(exiftool, records, choices) + removed_dirs = remove_empty_dirs(cleanup_roots, keep_dirs={working_dir.resolve()}) + print_removed_dirs(removed_dirs) + if failures: print("\nSome files failed:") for failure in failures: