Hello, I still don't have a good idea on how to efficiently implement this.
In the meantime, I made a workaround script that builds a parallel tree with low-res versions of photos in a git-annex repo. Here it is. License: GPL3. Enrico -- GPG key: 4096R/634F4BD1E7AD5568 2009-05-08 Enrico Zini <enr...@enricozini.org>
#!/usr/bin/python3 import os import errno import re import shutil from PIL import Image class FileInfo: re_image = re.compile(r"\.(?:png|jpg|tif)$", re.I) def __init__(self, fname, dir_fd): if not self.re_image.search(fname): self.is_image = False else: self.is_image = True try: self.dest = os.readlink(fname, dir_fd=dir_fd) self.is_symlink = True except OSError as e: if e.errno == errno.EINVAL: self.is_symlink = False self.dest = None else: raise if self.dest is not None: self.annexed = ".git/annex/objects" in self.dest else: self.annexed = False if self.annexed: try: os.stat(fname, dir_fd=dir_fd) self.available = True except OSError as e: if e.errno == errno.ENOENT: self.available = False else: raise @property def is_annexed_image(self): return self.is_image and self.annexed class Action: def __init__(self, src_root, dst_root): self.src_root = src_root self.dst_root = dst_root def dir(self, relpath, dst): pass def copy(self, relpath, dst): pass def scale(self, relpath, dst): pass class PrintAction(Action): def dir(self, relpath): print("mkdir", relpath) def copy(self, relpath, dst): print("copy", relpath, "to", dst) def scale(self, relpath, dst): print("scale", relpath, "to", dst) class RealAction(Action): def __init__(self, *args, **kw): super().__init__(*args, **kw) self.printer = PrintAction(self.src_root, self.dst_root) def dir(self, relpath): os.makedirs(os.path.join(self.dst_root, relpath), exist_ok=True) def copy(self, relpath): src = os.path.join(self.src_root, relpath) self.printer.copy(relpath, dst) shutil.copy2(src, dst, follow_symlinks=False) def scale(self, relpath): src = os.path.join(self.src_root, relpath) tmp_dst = os.path.join(os.path.dirname(dst), "tmp_" + os.path.basename(dst)) self.printer.scale(relpath, dst) try: img = Image.open(src, mode="r") except OSError as e: print("scale {} failed: {}".format(src, e)) return img.thumbnail((800, 800), Image.ANTIALIAS) img.save(tmp_dst) os.rename(tmp_dst, dst) class Scanner: def __init__(self, src_root, dst_root): self.src_root = src_root self.dst_root = dst_root def scan(self, action): root_dirfd = os.open(self.src_root, os.O_DIRECTORY) for root, dirs, files, rootfd in os.fwalk(".", dir_fd=root_dirfd): action.dir(root) for f in files: info = FileInfo(f, dir_fd=rootfd) if not info.annexed: continue if not info.available: continue dst = os.path.join(self.dst_root, root, f) if os.path.lexists(dst): continue if info.is_image: action.scale(os.path.join(root, f), dst) else: action.copy(os.path.join(root, f), dst) def main(): import argparse parser = argparse.ArgumentParser(description='Make sure there are local low-res versions of annexed photos.') parser.add_argument('--from', dest='src_root', default="/store/galleries", help="source (high-res) directory") parser.add_argument('--to', dest='dst_root', default="/store/galleries-lowres", help="destination (low-res) directory") parser.add_argument('-f', dest='perform', action="store_true", help="actually perform actions instead of printing") args = parser.parse_args() if args.perform: action = RealAction(args.src_root, args.dst_root) else: action = PrintAction(args.src_root, args.dst_root) scanner = Scanner(args.src_root, args.dst_root) scanner.scan(action) if __name__ == "__main__": main()
signature.asc
Description: PGP signature