#!/usr/bin/env python3 import os import sys import shutil import argparse SKIP_NAMES = set([".DS_Store"]) class Chunk: def __init__(self, path): self.path = path parts = self.path.split("/") self.key = f"{parts[-4]}T{parts[-1]} {parts[-5]}" self.size = dir_size(path) def __repr__(self): return f"{self.key} [{gib(self.size):.2f}GiB] ({self.path})" def gib(size): return size / 1024 / 1024 / 1024 def dir_size(path): size = 0 for dirpath, dirnames, filenames in os.walk(path): for f in filenames: fp = os.path.join(dirpath, f) if not os.path.islink(fp): size += os.path.getsize(fp) return size def find_empty(path): empty = set([]) for dirpath, dirnames, filenames in os.walk(path, topdown=False): filenames = list(filter(lambda name: name not in SKIP_NAMES, filenames)) if not dirnames and not filenames: empty.add(dirpath) return empty def cleanup_empty_dirs(path): empty = find_empty(path) check = set([]) for empty in find_empty(path): print(f"Removing empty directory {empty}") shutil.rmtree(empty) check.add(os.path.dirname(empty)) while check: next_check = set([]) for candidate in check: if not os.path.exists(candidate): continue items = list( filter(lambda name: name not in SKIP_NAMES, os.listdir(candidate)) ) if not items: print(f"Removing empty directory {candidate}") shutil.rmtree(candidate) next_check.add(os.path.dirname(candidate)) check = next_check def collect_chunks(path): chunks = [] for dirpath, dirnames, filenames in os.walk(path, topdown=False): if not dirpath.endswith("dav"): continue for dirname in dirnames: chunks.append(Chunk(os.path.join(dirpath, dirname))) print(f"Checked {dirpath}", file=sys.stderr) return chunks if __name__ == "__main__": parser = argparse.ArgumentParser( prog="Amcrest camera cleanup", description="Cleanup old videos from Amcrest cameras", ) parser.add_argument("path", help="Path to amcrest video files root") parser.add_argument("-s", "--size", required=True, help="Max size to keep in MiB") args = parser.parse_args() permitted_size = int(args.size) * 1024 * 1024 chunks = sorted(collect_chunks(args.path), key=lambda chunk: chunk.key) total_size = 0 for chunk in chunks: total_size += chunk.size print( f"Total size: {gib(total_size):.2f}GiB, max allowed: {gib(permitted_size):.2f}GiB", file=sys.stderr, ) while total_size > permitted_size: chunk = chunks.pop(0) print(f"Removing {chunk}", file=sys.stderr) total_size -= chunk.size shutil.rmtree(chunk.path) cleanup_empty_dirs(args.path)