From 33289bef2b08748db1c8061ecf453b291d2dc337 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Fri, 16 Oct 2020 12:52:36 +0200 Subject: [PATCH 1/1] initial commit --- .gitignore | 2 ++ src/misc.py | 39 +++++++++++++++++++++++++++++++++++++++ src/photosort.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 .gitignore create mode 100644 src/misc.py create mode 100755 src/photosort.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b5e2e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +__pycache__ diff --git a/src/misc.py b/src/misc.py new file mode 100644 index 0000000..9a78f4e --- /dev/null +++ b/src/misc.py @@ -0,0 +1,39 @@ +import mimetypes +import os +import shutil + +def walk_media_files(dir_path): + for root, dirs, files in os.walk(dir_path): + for f in files: + file_path = os.path.join(root, f) + if _is_media_file(file_path): + yield (f, file_path) + +def extract_timestamp(file_path): + return os.path.getmtime(file_path) + +def find_file(dir_path, file_name, file_size): + for root, dirs, files in os.walk(dir_path): + for f in files: + if f == file_name: + full_path = os.path.join(root, f) + if os.path.getsize(full_path) == file_size: + return root + return None + +def import_file(src_file_path, dst_file_path, move=False): + if move: + shutil.move(src_file_path, dst_file_path) + else: + shutil.copyfile(src_file_path, dst_file_path) + shutil.copystat(src_file_path, dst_file_path) + +def _is_media_file(file_path): + if not os.path.isfile(file_path): + return False + mime_type = mimetypes.guess_type(file_path)[0] + if not mime_type: + return False + if not mime_type.split('/')[0] in ['image', 'video']: + return False + return True diff --git a/src/photosort.py b/src/photosort.py new file mode 100755 index 0000000..38bea1b --- /dev/null +++ b/src/photosort.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import argparse +import datetime +import logging +import os + +import misc + +parser = argparse.ArgumentParser(description='Process some integers.') +parser.add_argument('SOURCE_DIR', type=str, help='source directory') +parser.add_argument('DEST_DIR', type=str, help='target directory') +parser.add_argument('-c', '--cleanup', action='store_true', dest='cleanup', + default=False, help='clean-up source dir') +parser.add_argument('-l', '--log-level', type=str, default='INFO', dest='log_lvl', + choices=['DEBUG', 'INFO', 'WARNING'], help='select log level') + +args = parser.parse_args() + +logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s', + level=logging.getLevelName(args.log_lvl), + datefmt='%m/%d/%Y %H:%M:%S') + +for src_file_name, src_file_path in misc.walk_media_files(args.SOURCE_DIR): + logging.info('Processing %s...', src_file_name) + + src_time = misc.extract_timestamp(src_file_path) + + dst_dir = os.path.join(args.DEST_DIR, + datetime.datetime.fromtimestamp(src_time).strftime("%Y/%m")) + dst_file_path = os.path.join(dst_dir, src_file_name) + + if not os.path.exists(dst_file_path): + alt_dst_dir = misc.find_file(args.DEST_DIR, + src_file_name, + os.path.getsize(src_file_path)) + if alt_dst_dir: + dst_dir = alt_dst_dir + dst_file_path = os.path.join(dst_dir, src_file_name) + + if not os.path.exists(dst_file_path): + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + misc.import_file(src_file_path, dst_file_path, move=args.cleanup) + else: + dst_time = misc.extract_timestamp(dst_file_path) + if src_time > dst_time: + misc.import_file(src_file_path, dst_file_path, move=args.cleanup) -- 2.39.5