import argparse import traceback from photoapp.image import get_jpg_info, get_hash, get_mtime, special_magic from itertools import chain from photoapp.types import Photo, PhotoSet, known_extensions, regular_images, files_raw, files_video, map_extension import os """ Photo sorting rules: jpeg exif date file modification date raw group with exif date of jpeg with same name file modification date mov, video, or other modification date """ def pprogress(done, total=None): print(" complete: {}{}\r".format(done, " / {} ".format(total) if total else ''), end='') def group_by_extension(files): byext = {k: [] for k in known_extensions} excluded = [] for item in files: if not os.path.isfile(item): # Not a file excluded.append(item) continue extension = item.split(".") if len(extension) < 2: # no extension excluded.append(item) continue extension = map_extension(extension[-1].lower()) if extension not in known_extensions: # an extension we don't support excluded.append(item) continue byext[extension.lower()].append(item) return (byext, excluded) def get_photosets(files): byext, skipped = group_by_extension(files) photosets = [] # process regular images first. for item in chain(*[byext[ext] for ext in regular_images]): photosets.append(get_jpg_info(item)) # process raws for item in chain(*[byext[ext] for ext in files_raw]): itemmeta = Photo(hash=get_hash(item), path=item, size=os.path.getsize(item), format=special_magic(item), fname=os.path.basename(item)) fprefix = os.path.basename(item)[::-1].split(".", 1)[-1][::-1] fmatch = "{}.jpg".format(fprefix.lower()) # if we're inspecting "foobar.raw", match it with "foobar.jpg" # TODO does this account for extension mappinh like jpeg->jpg? foundmatch = False for photo in photosets: for fmt in photo.files[:]: if os.path.basename(fmt.path).lower() == fmatch: foundmatch = True photo.files.append(itemmeta) break if foundmatch: break if not foundmatch: mtime = get_mtime(item) print("no match found for", itemmeta.path, "but importing anyway") photosets.append(PhotoSet(date=mtime, date_real=mtime, files=[itemmeta])) # TODO handle any xmp without an associated regular image or cr2 # process other known formats for item in chain(*[byext[ext] for ext in files_video]): itemmeta = Photo(hash=get_hash(item), path=item, size=os.path.getsize(item), format=special_magic(item), fname=os.path.basename(item)) mtime = get_mtime(item) photosets.append(PhotoSet(date=mtime, date_real=mtime, files=[itemmeta])) return photosets, skipped def batch_ingest(library, files): sets, skipped = get_photosets(files) print("\nUpdating database") done = 0 total = len(sets) for photoset in sets: try: library.add_photoset(photoset) pprogress(done, total) done += 1 except Exception: traceback.print_exc() pass print("\nUpdate complete") def main(): parser = argparse.ArgumentParser(description="Library ingestion tool") parser.add_argument("files", nargs="+") args = parser.parse_args() library = PhotoLibrary("photos.db", "./library", "./cache") batch_ingest(library, args.files) if __name__ == '__main__': main()