121 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| # usage: metasort SRCDIR DESTDIR
 | |
| 
 | |
| # this script iterates through every file in SRCDIR, copying them into a YYYY/MM/DD folder structure rooted in DESTDIR. 
 | |
| # EXIF data is read from jpg images. files with matching basenames to jpg images (.JPG.xmp, .ARW) are copied with their matching JPG images.
 | |
| 
 | |
| # TODO:
 | |
| #
 | |
| #   copy images and associated files into folders
 | |
| 
 | |
| import os
 | |
| import sys
 | |
| import re
 | |
| import pathlib
 | |
| import shutil
 | |
| import exifread
 | |
| import datetime
 | |
| 
 | |
| # parse and validate arguments
 | |
| 
 | |
| if len(sys.argv) != 3:
 | |
|     print("usage: metasort SRCDIR DESTDIR")
 | |
|     sys.exit(1)
 | |
| 
 | |
| src_dir = sys.argv[1]
 | |
| dest_dir = sys.argv[2]
 | |
| 
 | |
| def check_is_dir(path):
 | |
|     if not os.path.isdir(path):
 | |
|         print("{} is not a directory".format(path))
 | |
|         sys.exit(1)
 | |
| 
 | |
| check_is_dir(src_dir)
 | |
| check_is_dir(dest_dir)
 | |
| 
 | |
| # arguments ok!
 | |
| 
 | |
| src_filenames = next(os.walk(src_dir), (None, None, []))[2]
 | |
| src_filenames.sort()
 | |
| 
 | |
| jpg_pattern = re.compile("^(.+)(\.JPG)$")
 | |
| 
 | |
| bad_jpgs = []
 | |
| 
 | |
| def get_all_files_for_name(filename):
 | |
|     stem = pathlib.Path(filename).stem
 | |
|     stem_pattern = re.compile("^({})(\..+)$".format(stem))
 | |
| 
 | |
|     files = []
 | |
|     files.append(filename)
 | |
| 
 | |
|     for other_filename in src_filenames:
 | |
| 
 | |
|         if jpg_pattern.match(other_filename):
 | |
|             continue
 | |
| 
 | |
|         if stem_pattern.match(other_filename):
 | |
|             files.append(other_filename)
 | |
| 
 | |
|     return files
 | |
| 
 | |
| 
 | |
| copied = 0
 | |
| for src_filename in src_filenames:
 | |
| 
 | |
|     if not jpg_pattern.match(src_filename):
 | |
|         continue
 | |
| 
 | |
|     # read exif properties
 | |
|     path = os.path.join(src_dir, src_filename)
 | |
|     tags = exifread.process_file(open(path, 'rb'))
 | |
|     date_tag = "Image DateTime"
 | |
|     if not date_tag in tags:
 | |
|         bad_jpgs.append(src_filename)
 | |
|         continue
 | |
| 
 | |
|     date = datetime.datetime.strptime(str(tags[date_tag]), "%Y:%m:%d %H:%M:%S")
 | |
| 
 | |
|     # make a directory for this date 
 | |
|     date_dir = os.path.join(dest_dir, str(date.year), f'{date.month:02}', f'{date.day:02}')
 | |
|     pathlib.Path(date_dir).mkdir(parents=True, exist_ok=True)
 | |
| 
 | |
|     files = get_all_files_for_name(src_filename)
 | |
| 
 | |
|     print("\n{}\t{}\t{}\t{}\n".format(src_filename, len(files), date, date_dir))
 | |
| 
 | |
|     # copy all files into date_dir
 | |
|     for f in files:
 | |
|         f_src = os.path.join(src_dir, f)
 | |
|         f_dest = os.path.join(date_dir, f)
 | |
| 
 | |
|         print(f"{f_src}\t->\t{f_dest}")
 | |
|         shutil.copy2(f_src, f_dest)
 | |
|         copied += 1
 | |
| 
 | |
| # deal with bad files
 | |
| bad_dir = os.path.join(dest_dir, "corrupted")
 | |
| bad_files = []
 | |
| for bad_jpg in bad_jpgs:
 | |
|     for bad_file in get_all_files_for_name(bad_jpg):
 | |
|         bad_files.append(bad_file)
 | |
| 
 | |
| if len(bad_files) > 0:
 | |
|     print("could not read metadata from {} jpgs:\n".format(len(bad_jpgs)))
 | |
|     print(", ".join(bad_jpgs))
 | |
|     print("\nin total, {} corrupt files were found:\n".format(len(bad_files)))
 | |
|     print(", ".join(bad_files))
 | |
|     print("\nthese will be copied to {}. maybe they can be fixed eventually 😔".format(bad_dir))
 | |
| 
 | |
|     pathlib.Path(bad_dir).mkdir(parents=True, exist_ok=True)
 | |
| 
 | |
|     # copy bad files to corrupted dir
 | |
|     for bad_file in bad_files:
 | |
|         bad_file_src = os.path.join(src_dir, bad_file)
 | |
|         bad_file_dest = os.path.join(bad_dir, bad_file)
 | |
|         shutil.copy2(bad_file_src, bad_file_dest)
 | |
|         copied += 1
 | |
| 
 | |
| print(f"\nall done! {copied} files were copied in total. feel free to delete files in {src_dir}. have a nice day!")
 |