sorting mov files that are the iphone live photos. shitty code but comitting anyway

master
Benedikt Kristinsson 2 years ago
parent 90862cb771
commit 11d3deb66f
  1. 59
      movheaders.py
  2. 48
      sort-photos.py

@ -0,0 +1,59 @@
#!/usr/bin/python
from datetime import datetime
import struct
import sys
def get_mov_dates(filename):
# borrowed from stackoverflow
# https://stackoverflow.com/questions/21355316/getting-metadata-for-mov-video
ATOM_HEADER_SIZE = 8
# difference between Unix epoch and QuickTime epoch, in seconds
EPOCH_ADJUSTER = 2082844800
# open file and search for moov item
f = open(filename, "rb")
while 1:
atom_header = f.read(ATOM_HEADER_SIZE)
if atom_header[4:8] == b'moov':
break
else:
try:
atom_size = struct.unpack(">I", atom_header[0:4])[0]
f.seek(atom_size - 8, 1)
except struct.error:
raise ValueError("no 'moov' header found in {}".format(filename))
# found 'moov', look for 'mvhd' and timestamps
atom_header = f.read(ATOM_HEADER_SIZE)
if atom_header[4:8] == b'cmov':
print("moov atom is compressed")
elif atom_header[4:8] != b'mvhd':
print("expected to find 'mvhd' header")
else:
f.seek(4, 1)
creation_date = struct.unpack(">I", f.read(4))[0]
modification_date = struct.unpack(">I", f.read(4))[0]
created = datetime.utcfromtimestamp(creation_date - EPOCH_ADJUSTER)
#modified = datetime.utcfromtimestamp(modification_date - EPOCH_ADJUSTER)
# print(created)
#return modified
return created
def get_date(path):
"""Returns a tuple of strings (y, m, d) where m and d are zero-padded
what sort-photos.py expects"""
date = get_mov_dates(path)
y, m, d = [str(a).zfill(2) for a in [date.year, date.month, date.day]]
return (y, m, d)
if __name__ == "__main__":
path = sys.argv[1]
date = get_date(path)
print(date)

@ -7,6 +7,8 @@ import shutil
import exifread
import movheaders
HOME = os.getenv("HOME")
NEXTCLOUD_DIR = os.path.join(HOME, "Nextcloud")
PHOTOS_DIR = os.path.join(NEXTCLOUD_DIR, "Photos")
@ -18,6 +20,7 @@ parser.add_argument("--src", required=True)
parser.add_argument("--dst", default=PHOTOS_DIR, required=False)
parser.add_argument("--debug", action="store_true")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--fileext")
args = parser.parse_args()
def debug(s):
@ -33,29 +36,56 @@ def mkdir(path):
os.makedirs(path, exist_ok=True)
def get_date(path):
with open(path, 'rb') as f:
tags = exifread.process_file(f, stop_tag="EXIF DateTimeOriginal")
if os.path.splitext(path)[1].lower() == ".mov":
return movheaders.get_date(path)
else:
with open(path, 'rb') as f:
tags = exifread.process_file(f, stop_tag="EXIF DateTimeOriginal")
y, m, d = tags["EXIF DateTimeOriginal"].values.split(" ")[0].split(":")
return (y, m, d)
y, m, d = tags["EXIF DateTimeOriginal"].values.split(" ")[0].split(":")
return (y, m, d)
if __name__ == "__main__":
g = glob.glob(args.src)
#g = glob.glob(args.src)
# just one dir at a time
g = os.listdir(args.src)
# print(g)
# raise SystemExit
for a in g:
try:
y, m, _ = get_date(a)
# im gonna have fun with this next time i look at it
a_ext = os.path.splitext(a)[1].lower()
if args.fileext and a_ext != args.fileext:
continue
_path = os.path.join(args.src, a)
a_mb = os.path.getsize(_path) >> 20
#print("{}, {} mb".format(a, a_mb))
y, m, d = get_date(_path)
if args.fileext and a_ext == ".mov":
# big videos (actual videos ive taken) have already
# been uploaded by nextcloud. Trying to single out
# the live videos around photos
if a_mb > 5:
debug("Too big: {}, {} mb, {}/{}/{}".format(_path, a_mb, y, m, d))
# ignore completely, no wasting space on nextcloud
continue
ymdir = os.path.join(PHOTOS_DIR, y, m)
mkdir(ymdir)
debug("{} -> {}".format(a, ymdir))
move(a, ymdir)
move(_path, ymdir)
except ValueError:
mkdir(UNSORTED_DIR)
print("{} -> {}".format(a, UNSORTED_DIR))
move(os.path.join(args.src, a), UNSORTED_DIR)
except KeyError:
# Could not parse date
mkdir(UNSORTED_DIR)
print("{} -> {}".format(a, UNSORTED_DIR))
move(a, UNSORTED_DIR)
move(os.path.join(args.src, a), UNSORTED_DIR)
except IsADirectoryError:
# leave directories, happens if you didn't give the
# correct full path
print("Skipping directory: {}".format(a))
print("Skipping directory: {}".format(os.path.join(args.src, a)))
continue

Loading…
Cancel
Save