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()

Attachment: signature.asc
Description: PGP signature

Reply via email to