Skip to content

Instantly share code, notes, and snippets.

@jason19970210
Last active June 8, 2024 03:54
Show Gist options
  • Save jason19970210/32f80f2411f828ae413e3941ec5322e3 to your computer and use it in GitHub Desktop.
Save jason19970210/32f80f2411f828ae413e3941ec5322e3 to your computer and use it in GitHub Desktop.
Photo Rename with datetime ft. timezone
# -*- coding: utf-8 -*-
'''
$ adb devices
$ adb push /Users/macbook/.../* /sdcard/DCIM/Camera/
$ adb push /Users/macbook/.../*.JPG /sdcard/DCIM/Camera/
'''
'''
Pixel Unsupoort Upload Format: AAE
'''
'''
Usage: python3 photo_rename.py --dir <dir>
'''
import os
import re
from datetime import datetime, timedelta
from pytz import timezone
from pathlib import Path
from PIL import Image, ExifTags, UnidentifiedImageError
from pillow_heif import register_heif_opener
import argparse
# default ext list
ext_list = ["heic", "jpg", "png", "mov", "mp4"]
# def get_ext(ext: list[str] = None) -> list[str]:
# # if args.ext existed
# if ext:
# ext_list += ext
# return ext_list
# def parse_ext(ext: list[str]) -> str:
# '''
# return re match style string
# "^.(jpg|png|mp4)$"gm
# '''
# return f"^.({"|".join(ext)})$"
# def get_media(path: Path) -> list[str]:
# print(path.resolve())
# pass
# https://stackoverflow.com/a/51212150
def dir_path(path: str) -> Path:
p = Path(path)
if p.is_dir():
return p
# raise NotADirectoryError(string)
raise argparse.ArgumentTypeError(f"{p} is not a valid path")
def get_parser():
'''
usage: --dir <path> [ --ext <ext> ... ]
'''
parser = argparse.ArgumentParser(description='Convert & Standardize photos filename with EXIF information')
parser.add_argument('--dir', dest="dir", type=dir_path, required=True, help="the target directory where the media locate")
parser.add_argument('--ext', dest='ext', metavar='ext', type=str, nargs='+', help=f'the additional file extensions need included (default: {ext_list})')
parser.add_argument('-v', '--verbose', help='increase output verbosity', action="store_true")
return parser
def main():
parser = get_parser()
args = parser.parse_args()
dir: Path = args.dir
register_heif_opener()
for p in dir.rglob("*"):
if args.verbose:
print(f'filename: {p}')
# Device: Canon EOS 5D Mark II
if re.match(pattern="^.(cr2|jpg)$", string=p.suffix, flags=re.IGNORECASE):
'''
CR2:
{
256: 5616, 0x0100 ImageWidth
257: 3744, 0x0101 ImageLength
258: (8, 8, 8), 0x0102 BitsPerSample
259: 6, 0x0103 Compression
271: 'Canon', 0x010F Make
272: 'Canon EOS 5D Mark II', 0x0110 Model
273: 59572, 0x0111 StripOffsets
274: 1, 0x0112 Orientation
279: 2241375, 0x0117 StripByteCounts
282: 72.0, 0x011A XResolution
283: 72.0, 0x011B YResolution
296: 2, 0x0128 ResolutionUnit
306: '2024:05:13 08:15:59', 0x0132 DateTime // GMT+8
315: '', 0x013B Artist
33432: '', 0x8298 Copyright
34665: 434, 0x8769 ExifOffset
34853: 42354, 0x8825 GPSInfo
}
'''
'''
JPG:
{
271: 'Canon', 0x010F Make
272: 'Canon EOS 5D Mark II', 0x0110 Model
274: 1, 0x0112 Orientation
282: 72.0, 0x011A XResolution
283: 72.0, 0x011B YResolution
296: 2, 0x0128 ResolutionUnit
306: '2024:05:13 08:15:59', 0x0132 DateTime
315: '', 0x013B Artist
318: (0.313, 0.329), 0x013E WhitePoint
319: (0.64, 0.33, 0.21, 0.71, 0.15, 0.06), 0x013F PrimaryChromaticities
529: (0.299, 0.587, 0.114), 0x0211 YCbCrCoefficients
531: 2, 0x0213 YCbCrPositioning
33432: '', 0x8298 Copyright
34665: 484, 0x8769 ExifOffset
34853: 8816, 0x8825 GPSInfo
}
'''
img_exif = Image.open(p).getexif()
try:
dt = datetime.strptime(img_exif[ExifTags.Base.DateTime], "%Y:%m:%d %H:%M:%S")
tz = timezone("Asia/Taipei")
dt = tz.localize(dt).astimezone(timezone('Australia/Sydney')).isoformat()
stat = os.stat(p)
tz = timezone("Australia/Sydney")
atime = datetime.fromtimestamp(stat.st_atime) + timedelta(hours=4)
mtime = datetime.fromtimestamp(stat.st_mtime) + timedelta(hours=4)
# set atime & mtime for file
os.utime(p, (tz.localize(atime).timestamp(), tz.localize(mtime).timestamp()))
# rename the filename with dt
p.rename(f'{p.parent}/{p.stem}_{dt}{p.suffix}')
except Exception as e:
print(f'e: {e}')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment