Last active
June 8, 2024 03:54
-
-
Save jason19970210/32f80f2411f828ae413e3941ec5322e3 to your computer and use it in GitHub Desktop.
Photo Rename with datetime ft. timezone
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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