#!/usr/bin/env nix-shell #! nix-shell -i python3 --pure #! nix-shell -p python3 python3Packages.coloredlogs """ Same as picture_name_date except it's tailored for OpenCamera. It uses filenames, that way: - Easier to get metadata - JPG/DNG, MP4/SRT keep the same filename """ import argparse import logging import os import re import coloredlogs log = logging.getLogger(__name__) coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s", logger=log) PATTERNS = [ re.compile( # OpenCamera r"^(?P<type>IMG|VID)_" # No TRIM. We don't want those as they're a short copy of an existing VID. r"(?P<Y>\d\d\d\d)(?P<M>\d\d)(?P<D>\d\d)_" r"(?P<h>\d\d)(?P<m>\d\d)(?P<s>\d\d)" r"(?P<dup>_(\d+))?" r"(?P<spec>_(PANO|HDR))?" r"\.(?P<ext>jpg|dng|mp4|srt)$" ), re.compile( # Samsung camera app (?) r"(?P<Y>\d\d\d\d)(?P<M>\d\d)(?P<D>\d\d)_" r"(?P<h>\d\d)(?P<m>\d\d)(?P<s>\d\d)" r"\.(?P<ext>jpg|mp4)$" ), re.compile( # Telegram Media Downloader photo r"photo_" r"(?P<Y>\d\d\d\d)-(?P<M>\d\d)-(?P<D>\d\d)_" r"(?P<h>\d\d)-(?P<m>\d\d)-(?P<s>\d\d)" r"(?P<dup>_\d{19})" r"\.(?P<ext>jpg)$" ), # Time of publication, not time of photo! re.compile( # Telegram Media Downloader video r"video_" r"(?P<Y>\d\d\d\d)-(?P<M>\d\d)-(?P<D>\d\d)_" r"(?P<h>\d\d)-(?P<m>\d\d)-(?P<s>\d\d)" r"(?P<dup>_\d{19})" r"\.(?P<ext>mp4)$" ), # Time of publication, not time of video! ] def main(args: argparse.Namespace) -> None: for root, _, files in os.walk(args.dir): for filename in files: full_path = os.path.join(root, filename) for pattern in PATTERNS: match = re.match(pattern, filename) if match: break else: log.warning(f"{full_path} doesn't any pattern") continue # Build new filename m = match.groupdict() new_filename = ( f"{m['Y']}-{m['M']}-{m['D']}_" f"{m['h']}-{m['m']}-{m['s']}" f"{m.get('dup') or ''}" f"{m.get('spec') or ''}" f"{args.suffix}" f".{m['ext']}" ) new_path = os.path.join(args.dir, new_filename) # TODO Allow keeping image in same folder # 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"{new_path} already exists!") if not args.dry: os.rename(full_path, new_path) if __name__ == "__main__": parser = argparse.ArgumentParser( description="Rename OpenCamera files 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( "-s", "--suffix", default="", help="Text to add before the extension", ) args = parser.parse_args() main(args)