add logic for EXIF timestamp not being consistent

This commit is contained in:
Kameron Kenny 2024-08-20 11:02:59 -04:00
parent 25fcfdbb27
commit 827f057989
No known key found for this signature in database
GPG Key ID: E5006629839D2276
2 changed files with 55 additions and 24 deletions

79
import_media.py Normal file → Executable file
View File

@ -1,21 +1,21 @@
#!/usr/bin/env python #!/usr/bin/env python
''' """
Import photos from SD card into folder with todays date + nickname Import photos from SD card into folder with today's date + nickname
Use: importphotos (--jpg|--raw|--both) <nickname of folder (optional)> Use: import_media.py (--jpg|--raw|--both) <nickname of folder (optional)>
Add script to path Add script to path
TODO: TODO:
8. Optinally allow specification of a backup location on another disk 8. Optionally allow specification of a backup location on another disk
or NAS to ship a 3rd copy to or NAS to ship a 3rd copy to
10. Every config option has an arg override 10. Every config option has an arg override
11. Optionally rename file if EVENT name was passed in 11. Optionally rename file if EVENT name was passed in
-- STRETCH -- -- STRETCH --
12. Make a graphical interface 12. Make a graphical interface
''' """
import os import os
import sys
from pprint import pprint from pprint import pprint
import argparse import argparse
import shutil import shutil
@ -47,6 +47,8 @@ parser.add_argument("-b", "--backup-destination", help = "Create a backup of e
specified location") specified location")
parser.add_argument("-D", "--delete-source-files", help = "Delete files from SD after validating \ parser.add_argument("-D", "--delete-source-files", help = "Delete files from SD after validating \
checksum of copied files") checksum of copied files")
parser.add_argument("-v", "--verify", help = "[True|False] Verify the checksum of \
the copied file")
parser.add_argument("-c", "--config", help = "Load the specified config file instead \ parser.add_argument("-c", "--config", help = "Load the specified config file instead \
of the default " + CONFIG_FILE) of the default " + CONFIG_FILE)
parser.add_argument("-g", "--generate-config", help = "Generate config file based on options \ parser.add_argument("-g", "--generate-config", help = "Generate config file based on options \
@ -62,7 +64,7 @@ if args.source:
config['folders']['source']['base'] = args.source config['folders']['source']['base'] = args.source
if args.destination: if args.destination:
config['folders']['destination']['base'] = args.source config['folders']['destination']['base'] = args.source
#if args.create-oringinals: #if args.create-originals:
# pass # pass
#if args.backup-destination: #if args.backup-destination:
# pass # pass
@ -80,10 +82,13 @@ def dump_yaml(dictionary, file):
def md5_hash(file): def md5_hash(file):
""" calculates and returns md5 hash """ """ calculates and returns md5 hash """
#print("calculating md5 for ", f) if config['verify_checksum']:
md5 = hashlib.md5(open(file, 'rb').read()).hexdigest() #print("calculating md5 for ", f)
#with open(file, 'r') as f: md5 = hashlib.md5(open(file, 'rb').read()).hexdigest()
# md5 = hashlib.md5(f).hexdigest() #with open(file, 'r') as f:
# md5 = hashlib.md5(f).hexdigest()
else:
md5 = 'no_verify'
return md5 return md5
def cmp_files(file_1,file_2): def cmp_files(file_1,file_2):
@ -93,19 +98,45 @@ def cmp_files(file_1,file_2):
def get_capture_date(path, f_type): def get_capture_date(path, f_type):
""" get capture date from meta """ """ get capture date from meta """
if f_type == 'image': if f_type == 'image':
with open(path, 'rb') as file: with open(path, "rb") as file:
tags = exifread.process_file(file) tags = exifread.process_file(file)
if 'EXIF DateTimeOriginal' in tags: if 'EXIF DateTimeOriginal' in tags:
stamp = datetime.strptime(str(tags['EXIF DateTimeOriginal']), '%Y:%m:%d %H:%M:%S') try:
stamp = datetime.strptime(
str(tags['EXIF DateTimeOriginal']),
'%Y:%m:%d %H:%M:%S')
except ValueError as ve_dte:
print(f"\nError: {ve_dte}")
print("\nTrying digitized")
try:
stamp = datetime.strptime(
str(tags['EXIF DateTimeDigitized']),
'%Y:%m:%d %H:%M:%S')
except ValueError as ve_dtd:
print(f"\nError: {ve_dtd}")
print("\nTrying Image DateTime")
try:
stamp = datetime.strptime(
str(tags['Image DateTime']),
'%Y:%m:%d %H:%M:%S')
except ValueError as ve_idt:
print(f"\nError: {ve_idt}")
print(f"\nGiving up... Please inspect {path} and try again\n")
sys.exit()
elif 'Image DateTime' in tags: elif 'Image DateTime' in tags:
stamp = datetime.strptime(str(tags['Image DateTime']), '%Y:%m:%d %H:%M:%S') stamp = datetime.strptime(
str(tags['Image DateTime']), '%Y:%m:%d %H:%M:%S')
else: else:
stamp = datetime.strptime(str('1900:01:01 00:00:00'), '%Y:%m:%d %H:%M:%S') stamp = datetime.strptime(
str('1900:01:01 00:00:00'), '%Y:%m:%d %H:%M:%S')
elif f_type == 'video': elif f_type == 'video':
stamp = datetime.strptime(ffmpeg.probe(path)['format']['tags']['creation_time'], stamp = datetime.strptime(
ffmpeg.probe(path)['format']['tags']['creation_time'],
'%Y-%m-%dT%H:%M:%S.%f%z') '%Y-%m-%dT%H:%M:%S.%f%z')
elif f_type == 'audio': elif f_type == 'audio':
stamp = datetime.strptime(ffmpeg.probe(path)['format']['tags']['date'], '%Y-%m-%d') stamp = datetime.strptime(
ffmpeg.probe(path)['format']['tags']['date'], '%Y-%m-%d')
else: else:
stamp = datetime.fromtimestamp(os.path.getctime(path)) stamp = datetime.fromtimestamp(os.path.getctime(path))
@ -233,7 +264,7 @@ def process_file(path, f_type, f_name, ext):
files[i]['folders']['destination'] = files[i]['folders']['destination'] + '/AUDIO' files[i]['folders']['destination'] = files[i]['folders']['destination'] + '/AUDIO'
else: else:
print('WARN: ', files[i]['type'], \ print('WARN: ', files[i]['type'],
' is not a known type and you never should have landed here.') ' is not a known type and you never should have landed here.')
def find_files(directory): def find_files(directory):
@ -243,11 +274,11 @@ def find_files(directory):
for ext in config['file_types'][f_type]: for ext in config['file_types'][f_type]:
for file in tqdm(filename, desc = 'Finding ' + ext + ' Files', ncols = 100): for file in tqdm(filename, desc = 'Finding ' + ext + ' Files', ncols = 100):
if file.lower().endswith(ext): if file.lower().endswith(ext):
print(file) # print(file)
process_file(folder, f_type, file, ext) process_file(folder, f_type, file, ext)
def validate_config_dir_access(): def validate_config_dir_access():
""" Validate we can op in the defined directories """ """ Validate we can operate in the defined directories """
check = path_access_write(config['folders']['destination']['base']) check = path_access_write(config['folders']['destination']['base'])
if check is False: if check is False:
writable = False writable = False
@ -300,19 +331,19 @@ def validate_checksums():
for checksum in files[file]['md5_checksums']: for checksum in files[file]['md5_checksums']:
c[i] = files[file]['md5_checksums'][checksum] c[i] = files[file]['md5_checksums'][checksum]
if i > 0: if i > 0:
P = i - 1 p = i - 1
if c[i] == c[P]: if c[i] == c[p]:
files[file]['source_cleanable'] = True files[file]['source_cleanable'] = True
else: else:
files[file]['source_cleanable'] = False files[file]['source_cleanable'] = False
print(f'FATAL: Checksum validation failed for: \ print(f'FATAL: Checksum validation failed for: \
{files[file]["name"]} \n{c[i]}\n is not equal to \n{c[P]}\n') {files[file]["name"]} \n{c[i]}\n is not equal to \n{c[p]}\n')
print('\n File Meta:\n') print('\n File Meta:\n')
pprint(files[file]) pprint(files[file])
i = i + 1 i = i + 1
def cleanup_sd(): def cleanup_sd():
""" If we should cleanup the SD, nuke the copied files. """ """ If we should clean up the SD, nuke the copied files. """
if config['cleanup_sd'] is True: if config['cleanup_sd'] is True:
for file in tqdm(files, desc = "Cleaning Up SD:", ncols = 100): for file in tqdm(files, desc = "Cleaning Up SD:", ncols = 100):
if files[file]['source_cleanable'] is True: if files[file]['source_cleanable'] is True:

0
import_media_ui.py Normal file
View File