feat: Unify Driveのコピー処理を実装
This commit is contained in:
Vendored
+140
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Read Exif metadata from image files
|
||||
Supported formats: TIFF, JPEG, PNG, Webp, HEIC
|
||||
"""
|
||||
|
||||
from typing import Any, BinaryIO, Dict
|
||||
|
||||
from exifread.core.exceptions import ExifNotFound, InvalidExif
|
||||
from exifread.core.exif_header import ExifHeader
|
||||
from exifread.core.find_exif import determine_type, get_endian_str
|
||||
from exifread.core.xmp import find_xmp_data
|
||||
from exifread.exif_log import get_logger
|
||||
from exifread.serialize import convert_types
|
||||
from exifread.tags import DEFAULT_STOP_TAG
|
||||
|
||||
__version__ = "3.5.1"
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
def _extract_xmp_data(hdr: ExifHeader, fh: BinaryIO):
|
||||
# Easy we already have them
|
||||
xmp_tag = hdr.tags.get("Image ApplicationNotes")
|
||||
if xmp_tag:
|
||||
logger.debug("XMP present in Exif")
|
||||
xmp_bytes = bytes(xmp_tag.values)
|
||||
# We need to look in the entire file for the XML
|
||||
else:
|
||||
xmp_bytes = find_xmp_data(fh)
|
||||
if xmp_bytes:
|
||||
hdr.parse_xmp(xmp_bytes)
|
||||
|
||||
|
||||
def process_file(
|
||||
fh: BinaryIO,
|
||||
stop_tag: str = DEFAULT_STOP_TAG,
|
||||
details=True,
|
||||
strict=False,
|
||||
debug=False,
|
||||
truncate_tags=True,
|
||||
auto_seek=True,
|
||||
extract_thumbnail=True,
|
||||
builtin_types=False,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Process an image file to extract EXIF metadata.
|
||||
|
||||
This is the function that has to deal with all the arbitrary nasty bits
|
||||
of the EXIF standard.
|
||||
|
||||
:param fh: the file to process, must be opened in binary mode.
|
||||
:param stop_tag: Stop processing when the given tag is retrieved.
|
||||
:param details: If `True`, process MakerNotes.
|
||||
:param strict: If `True`, raise exceptions on errors.
|
||||
:param debug: Output debug information.
|
||||
:param truncate_tags: If `True`, truncate the `printable` tag output.
|
||||
There is no effect on tag `values`.
|
||||
:param auto_seek: If `True`, automatically `seek` to the start of the file.
|
||||
:param extract_thumbnail: If `True`, extract the JPEG thumbnail.
|
||||
The thumbnail is not always present in the EXIF metadata.
|
||||
:param builtin_types: If `True`, convert tags to standard Python types.
|
||||
|
||||
:returns: A `dict` containing the EXIF metadata.
|
||||
The keys are a string in the format `"IFD_NAME TAG_NAME"`.
|
||||
If `builtin_types` is `False`, the value will be a `IfdTag` class, or bytes.
|
||||
IF `builtin_types` is `True`, the value will be a standard Python type.
|
||||
"""
|
||||
|
||||
if auto_seek:
|
||||
fh.seek(0)
|
||||
|
||||
try:
|
||||
offset, endian_bytes, fake_exif = determine_type(fh)
|
||||
except ExifNotFound as err:
|
||||
logger.warning(err)
|
||||
return {}
|
||||
except InvalidExif as err:
|
||||
logger.debug(err)
|
||||
return {}
|
||||
|
||||
endian_str, endian_type = get_endian_str(endian_bytes)
|
||||
# deal with the EXIF info we found
|
||||
logger.debug("Endian format is %s (%s)", endian_str, endian_type)
|
||||
|
||||
hdr = ExifHeader(
|
||||
fh, endian_str, offset, fake_exif, strict, debug, details, truncate_tags
|
||||
)
|
||||
thumb_ifd = 0
|
||||
ctr = 0
|
||||
for ifd in hdr.list_ifd():
|
||||
if ctr == 0:
|
||||
ifd_name = "Image"
|
||||
elif ctr == 1:
|
||||
ifd_name = "Thumbnail"
|
||||
thumb_ifd = ifd
|
||||
else:
|
||||
ifd_name = "IFD %d" % ctr
|
||||
logger.debug("IFD %d (%s) at offset %s:", ctr, ifd_name, ifd)
|
||||
hdr.dump_ifd(ifd=ifd, ifd_name=ifd_name, stop_tag=stop_tag)
|
||||
ctr += 1
|
||||
# EXIF IFD
|
||||
exif_off = hdr.tags.get("Image ExifOffset")
|
||||
if exif_off:
|
||||
logger.debug("Exif SubIFD at offset %s:", exif_off.values[0])
|
||||
hdr.dump_ifd(ifd=exif_off.values[0], ifd_name="EXIF", stop_tag=stop_tag)
|
||||
|
||||
# EXIF SubIFD
|
||||
sub_ifds = hdr.tags.get("Image SubIFDs")
|
||||
if details and sub_ifds:
|
||||
for subifd_id, subifd_offset in enumerate(sub_ifds.values):
|
||||
logger.debug("Exif SubIFD%d at offset %d:", subifd_id, subifd_offset)
|
||||
hdr.dump_ifd(
|
||||
ifd=subifd_offset, ifd_name=f"EXIF SubIFD{subifd_id}", stop_tag=stop_tag
|
||||
)
|
||||
|
||||
# deal with MakerNote contained in EXIF IFD
|
||||
# (Some apps use MakerNote tags but do not use a format for which we
|
||||
# have a description, do not process these).
|
||||
if details and "EXIF MakerNote" in hdr.tags and "Image Make" in hdr.tags:
|
||||
try:
|
||||
hdr.decode_maker_note()
|
||||
except ValueError as err:
|
||||
if not strict:
|
||||
logger.debug("Failed to decode EXIF MakerNote: %s", str(err))
|
||||
else:
|
||||
raise err
|
||||
|
||||
# extract thumbnails
|
||||
if thumb_ifd and extract_thumbnail:
|
||||
hdr.extract_tiff_thumbnail(thumb_ifd)
|
||||
hdr.extract_jpeg_thumbnail()
|
||||
|
||||
# parse XMP tags (experimental)
|
||||
if debug and details:
|
||||
_extract_xmp_data(hdr=hdr, fh=fh)
|
||||
|
||||
if builtin_types:
|
||||
return convert_types(hdr.tags)
|
||||
|
||||
return hdr.tags
|
||||
Reference in New Issue
Block a user