168 lines
4.6 KiB
Python
168 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
#
|
|
# Library to extract Exif information from digital camera image files.
|
|
# https://github.com/ianare/exif-py
|
|
#
|
|
#
|
|
# Copyright (c) 2002-2007 Gene Cash
|
|
# Copyright (c) 2007-2025 Ianaré Sévi and contributors
|
|
#
|
|
# See LICENSE.txt file for licensing information
|
|
# See ChangeLog.rst file for all contributors and changes
|
|
#
|
|
|
|
"""
|
|
Runs Exif tag extraction in command line.
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import timeit
|
|
|
|
from exifread import __version__, exif_log, process_file
|
|
from exifread.core.exceptions import ExifError
|
|
from exifread.tags.fields import FIELD_DEFINITIONS
|
|
|
|
logger = exif_log.get_logger()
|
|
|
|
|
|
def get_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
prog="EXIF.py", description="Extract EXIF information from digital image files."
|
|
)
|
|
parser.add_argument(
|
|
"files",
|
|
metavar="FILE",
|
|
type=str,
|
|
nargs="+",
|
|
help="files to process",
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
"--version",
|
|
action="version",
|
|
version="EXIF.py Version %s on Python%s" % (__version__, sys.version_info[0]),
|
|
help="Display version information and exit",
|
|
)
|
|
parser.add_argument(
|
|
"-q",
|
|
"--quick",
|
|
action="store_false",
|
|
dest="detailed",
|
|
help="Do not process MakerNotes and do not extract thumbnails",
|
|
)
|
|
parser.add_argument(
|
|
"-t",
|
|
"--tag",
|
|
type=str,
|
|
dest="stop_tag",
|
|
help="Stop processing when this tag is retrieved.",
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--strict",
|
|
action="store_true",
|
|
dest="strict",
|
|
help="Run in strict mode (stop on errors).",
|
|
)
|
|
parser.add_argument(
|
|
"-b",
|
|
"--builtin",
|
|
action="store_true",
|
|
dest="builtin_types",
|
|
help="Convert IfdTag values to built-in Python variable types",
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--debug",
|
|
action="store_true",
|
|
dest="debug",
|
|
help="Run in debug mode (display extra info).",
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--color",
|
|
action="store_true",
|
|
dest="color",
|
|
help="Output in color (only works with debug on POSIX).",
|
|
)
|
|
args = parser.parse_args()
|
|
return args
|
|
|
|
|
|
def run_cli(args: argparse.Namespace) -> None:
|
|
"""Extract tags based on options (args)."""
|
|
|
|
exif_log.setup_logger(args.debug, args.color)
|
|
|
|
# output info for each file
|
|
for filename in args.files:
|
|
# avoid errors when printing to console
|
|
escaped_fn = filename.encode(
|
|
sys.getfilesystemencoding(), "surrogateescape"
|
|
).decode()
|
|
|
|
file_start = timeit.default_timer()
|
|
try:
|
|
with open(escaped_fn, "rb") as img_file:
|
|
logger.info("Opening: %s", escaped_fn)
|
|
|
|
tag_start = timeit.default_timer()
|
|
# get the tags
|
|
data = process_file(
|
|
img_file,
|
|
stop_tag=args.stop_tag,
|
|
details=args.detailed,
|
|
strict=args.strict,
|
|
debug=args.debug,
|
|
extract_thumbnail=args.detailed,
|
|
builtin_types=args.builtin_types,
|
|
)
|
|
tag_stop = timeit.default_timer()
|
|
|
|
except IOError:
|
|
logger.error("'%s' is unreadable", escaped_fn)
|
|
continue
|
|
|
|
if not data:
|
|
logger.warning("No EXIF information found")
|
|
print()
|
|
continue
|
|
|
|
if "JPEGThumbnail" in data:
|
|
logger.info("File has JPEG thumbnail")
|
|
del data["JPEGThumbnail"]
|
|
if "TIFFThumbnail" in data:
|
|
logger.info("File has TIFF thumbnail")
|
|
del data["TIFFThumbnail"]
|
|
|
|
for field in sorted(data):
|
|
value = data[field]
|
|
try:
|
|
if args.builtin_types:
|
|
logger.info("%s (%s): %r", field, type(value).__name__, value)
|
|
else:
|
|
logger.info(
|
|
"%s (%s): %s",
|
|
field,
|
|
FIELD_DEFINITIONS[value.field_type][1],
|
|
value.printable,
|
|
)
|
|
except (ExifError, ValueError):
|
|
logger.error("%s: %s", field, str(value))
|
|
|
|
file_stop = timeit.default_timer()
|
|
|
|
logger.debug("Tags processed in %s seconds", tag_stop - tag_start)
|
|
logger.debug("File processed in %s seconds", file_stop - file_start)
|
|
print()
|
|
|
|
|
|
def main() -> None:
|
|
run_cli(get_args())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|