Move scripts dir inside hm
And remove weird path contraptions
This commit is contained in:
parent
050901da2f
commit
edeef96133
49 changed files with 2 additions and 11 deletions
114
hm/scripts/picture_name_date
Executable file
114
hm/scripts/picture_name_date
Executable file
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 --pure
|
||||
#! nix-shell -p python3 python3Packages.coloredlogs python3Packages.exifread
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import typing
|
||||
|
||||
import coloredlogs
|
||||
import exifread
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s", logger=log)
|
||||
|
||||
EXTENSION_PATTERN = re.compile(r"\.(JPE?G|DNG)", re.I)
|
||||
COMMON_PATTERN = re.compile(r"(IMG|DSC[NF]?|100|P10|f|t)_?\d+", re.I)
|
||||
EXIF_TAG_ID = 0x9003 # DateTimeOriginal
|
||||
EXIF_DATE_FORMAT = "%Y:%m:%d %H:%M:%S"
|
||||
|
||||
|
||||
def get_pictures(directory: str = ".", skip_renamed: bool = True) -> typing.Generator:
|
||||
for root, _, files in os.walk(directory):
|
||||
for filename in files:
|
||||
filename_trunk, extension = os.path.splitext(filename)
|
||||
|
||||
if not re.match(EXTENSION_PATTERN, extension):
|
||||
continue
|
||||
if skip_renamed:
|
||||
if not re.match(COMMON_PATTERN, filename_trunk):
|
||||
continue
|
||||
full_path = os.path.join(root, filename)
|
||||
yield full_path
|
||||
|
||||
|
||||
def main(args: argparse.Namespace) -> None:
|
||||
log.warning("Counting files...")
|
||||
kwargs = {"directory": args.dir, "skip_renamed": args.skip_renamed}
|
||||
log.warning("Processing files...")
|
||||
for full_path in get_pictures(**kwargs):
|
||||
# Find date
|
||||
with open(full_path, "rb") as fd:
|
||||
exif_data = exifread.process_file(fd)
|
||||
if not exif_data:
|
||||
log.warning(f"{full_path} does not have EXIF data")
|
||||
for ifd_tag in exif_data.values():
|
||||
if ifd_tag.tag == EXIF_TAG_ID:
|
||||
date_raw = ifd_tag.values
|
||||
break
|
||||
else:
|
||||
log.warning(f"{full_path} does not have required EXIF tag")
|
||||
continue
|
||||
date = datetime.datetime.strptime(date_raw, EXIF_DATE_FORMAT)
|
||||
|
||||
# Determine new filename
|
||||
ext = os.path.splitext(full_path)[1].lower()
|
||||
if ext == ".jpeg":
|
||||
ext = ".jpg"
|
||||
new_name = date.isoformat().replace(":", "-").replace("T", "_") + args.suffix
|
||||
# First substitution is to allow images being sent to a NTFS filesystem
|
||||
# Second substitution is for esthetics
|
||||
new_path = os.path.join(args.dir, f"{new_name}{ext}")
|
||||
# TODO Allow keeping image in same folder
|
||||
i = 0
|
||||
while os.path.exists(new_path):
|
||||
if full_path == new_path:
|
||||
break
|
||||
log.debug(f"{new_path} already exists, incrementing")
|
||||
i += 1
|
||||
new_path = os.path.join(args.dir, f"{new_name}_{i}{ext}")
|
||||
|
||||
# Rename file
|
||||
if full_path == new_path:
|
||||
log.debug(f"{full_path} already at required filename")
|
||||
continue
|
||||
log.info(f"{full_path} →\t{new_path}")
|
||||
if os.path.exists(new_path):
|
||||
raise FileExistsError(f"Won't overwrite {new_path}")
|
||||
if not args.dry:
|
||||
os.rename(full_path, new_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Rename images based on their dates")
|
||||
parser.add_argument(
|
||||
"dir",
|
||||
metavar="DIRECTORY",
|
||||
type=str,
|
||||
default=".",
|
||||
nargs="?",
|
||||
help="Directory containing the pictures",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dry",
|
||||
action="store_true",
|
||||
help="Do not actually rename, just show old and new path",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--skip-renamed",
|
||||
action="store_true",
|
||||
help="Skip images whose filename doesn't match usual camera output filenames.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--suffix",
|
||||
default="",
|
||||
help="Text to add before the extension",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
main(args)
|
Loading…
Add table
Add a link
Reference in a new issue