tagkit.image.collection

Collection of EXIF image data for multiple files.

This module provides the ExifImageCollection class for working with EXIF data from multiple image files.

Module Contents

Classes

ExifImageCollection

A collection of EXIF data for multiple image files.

API

class tagkit.image.collection.ExifImageCollection(files: Iterable[tagkit.core.types.FilePath], *, tag_filter: Optional[Iterable[Union[int, str]]] = None, ifd: Optional[tagkit.core.types.IfdName] = None)

A collection of EXIF data for multiple image files.

This class provides a convenient way to access EXIF data from multiple files.

Args:

files: List of paths to image files. tag_filter: Optional list of tag names or IDs to filter by. ifd: Specific IFD to use.

Attributes:

files (Dict[str, ExifImage]): Dictionary mapping file paths to their EXIF data.

Initialization

Initialize the collection with a list of file paths.

Args:

files: List of paths to image files. tag_filter: Optional list of tag names or IDs to filter by. ifd: Specific IFD to use.

as_dict(binary_format: Optional[str] = None) dict[str, dict[str, dict[str, Union[str, int]]]]

Convert the collection to a dictionary.

Args:
binary_format: Format for binary data (‘hex’, ‘base64’, or None for default).

If None, <bytes: N> will be shown as a placeholder.

Returns:

Dictionary mapping file paths to their EXIF data dictionaries.

Example:
>>> collection = ExifImageCollection(["image2.jpg", "image3.jpg"])
>>> collection.as_dict()
{'image2.jpg': {'Make': {'id': 271, 'value': 'Tagkit', 'ifd': 'IFD0'}, 'DateTime': {'id': 306, 'value': '2025:05:02 14:30:00', 'ifd': 'IFD0'}}, 'image3.jpg': {'Make': {'id': 271, 'value': 'Tagkit', 'ifd': 'IFD0'}}}
property n_tags: int

Get the total number of tags across all files.

Returns:

Total number of tags.

Example:
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.n_tags
11
property n_files: int

Get the number of files in the collection.

Returns:

Number of files.

Example:
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.n_files
2
_normalize_filenames(files: Optional[Iterable[tagkit.core.types.FilePath]]) list[str]

Normalize file names to string keys and validate their presence in the collection.

Args:

files: Iterable of file paths (can be strings or Path objects).

Returns:

List of normalized file names (strings).

Raises:

KeyError: If a file is not found in the collection.

write_tag(tag_key: Union[str, int], value: tagkit.core.types.TagValue, ifd: Optional[tagkit.core.types.IfdName] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None)

Set the value of a specific EXIF tag for all or selected images in the collection.

Args:

tag_key: Tag name or tag ID. value: Value to set. ifd: Specific IFD to use. files: Iterable of file names (keys in self.files) to update. If None, update all.

Raises:

KeyError: If a file is not found in the collection. ValueError: If the tag or IFD is invalid.

Example:
>>> from tagkit.image.collection import ExifImageCollection
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.write_tag('Artist', 'John Doe', ifd='IFD0')
write_tags(tags: Mapping[Union[str, int], tagkit.core.types.TagValue], ifd: Optional[tagkit.core.types.IfdName] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None)

Set multiple EXIF tags for all or selected images in the collection.

Args:

tags: A dictionary mapping tag names or IDs to values. ifd: Specific IFD to use for all tags. files: Iterable of file names (keys in self.files) to update. If None, update all.

Example:
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.write_tags({'Artist': 'Jane', 'Copyright': '2025 John'})
delete_tag(tag_key: Union[str, int], ifd: Optional[tagkit.core.types.IfdName] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None)

Remove a specific EXIF tag from all or selected images in the collection. If a file does not contain the tag, it is silently ignored.

Args:

tag_key: Tag name or tag ID. ifd: Specific IFD to use. files: Iterable of file names (keys in self.files) to update. If None, update all.

Raises:

KeyError: If a file is not found in the collection. ValueError: If the tag or IFD is invalid.

Example:
>>> from tagkit.image.collection import ExifImageCollection
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.write_tag('Artist', 'John Doe', ifd='IFD0')
>>> collection.delete_tag('Artist', ifd='IFD0')
delete_tags(tag_keys: Iterable[Union[str, int]], ifd: Optional[tagkit.core.types.IfdName] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None)

Remove multiple EXIF tags from all or selected images in the collection. If a file does not contain a tag, it is silently ignored.

Args:

tag_keys: A list of tag names or tag IDs to remove. ifd: Specific IFD to use for all tags. files: Iterable of file names (keys in self.files) to update. If None, update all.

Example:
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.delete_tags(['Artist', 'Copyright'])
save_all(create_backup: bool = False)

Save all modified EXIF data back to their respective image files.

Args:

create_backup: If True, create a backup of each file before saving.

Example:
>>> from tagkit.image.collection import ExifImageCollection
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.write_tag('Artist', 'John Doe')
>>> collection.save_all(create_backup=True)
get_datetime(files: Optional[Iterable[tagkit.core.types.FilePath]] = None, tag: Optional[str] = None) dict[str, datetime.datetime]

Get datetime from EXIF tags for all or selected images in the collection.

Args:

files: Iterable of file names to query. If None, queries all files. tag: Optional specific datetime tag name to retrieve. If tag is None, precedence order is used to select the most relevant datetime tag (DateTimeOriginal > DateTimeDigitized > DateTime).

Returns:

Dictionary mapping file names to datetime objects (or None if not found).

Example:
>>> collection = ExifImageCollection(['image1.jpg', 'image2.jpg'])
>>> datetimes = collection.get_datetime()
>>> for filename, dt in datetimes.items():
...     print(f"{filename}: {dt}")
image1.jpg: 2025-05-01 14:30:00
image2.jpg: 2025-05-02 14:30:00
set_datetime(dt: datetime.datetime, tags: Optional[Iterable[str]] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None) None

Set datetime EXIF tags for all or selected images (in-memory, not saved).

Args:

dt: Datetime object to set. tags: Optional list of specific datetime tag names to update. If None, updates all three datetime tags. files: Iterable of file names to update. If None, updates all files.

Example:
>>> from datetime import datetime
>>> collection = ExifImageCollection(['image1.jpg', 'image2.jpg'])
>>> collection.set_datetime(datetime(2025, 6, 15, 10, 30, 0))
>>> collection.save_all()
offset_datetime(delta: datetime.timedelta, tags: Optional[Iterable[str]] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None) None

Offset datetime EXIF tags by a timedelta for all or selected images (in-memory).

Args:

delta: Timedelta to add to existing datetime values. tags: Optional list of specific datetime tag names to offset. If None, offsets all present datetime tags. files: Iterable of file names to update. If None, updates all files.

Example:
>>> from datetime import timedelta
>>> collection = ExifImageCollection(['image1.jpg', 'image2.jpg'])
>>> collection.offset_datetime(timedelta(hours=-5))
>>> collection.save_all()
get_all_datetimes(files: Optional[Iterable[tagkit.core.types.FilePath]] = None) dict[str, dict[str, datetime.datetime]]

Get all datetime EXIF tags for all or selected images in the collection.

Args:

files: Iterable of file names to query. If None, queries all files.

Returns:

Dictionary mapping file names to dictionaries of datetime tags.

Example:
>>> collection = ExifImageCollection(['image1.jpg', 'image2.jpg'])
>>> all_datetimes = collection.get_all_datetimes()
>>> for filename, datetimes in all_datetimes.items():
...     print(f"{filename}:")
...     for tag_name, dt in datetimes.items():
...         print(f"  {tag_name}: {dt}")
image1.jpg:
  DateTimeOriginal: 2025-05-01 14:30:00
  DateTime: 2025-05-01 14:30:00
image2.jpg:
  DateTime: 2025-05-02 14:30:00
read_tag(tag_key: Union[str, int], ifd: Optional[tagkit.core.types.IfdName] = None, format_value: bool = False, binary_format: Optional[str] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None, skip_missing: bool = False) dict[str, tagkit.core.types.TagValue]

Read the value of a specific EXIF tag from all or selected images in the collection.

Args:

tag_key: Tag name or tag ID. ifd: Specific IFD to use. format_value: If True, return formatted string values; if False, return raw values. binary_format: How to format binary data - ‘bytes’, ‘hex’, or ‘base64’. Only used when format_value=True. files: Iterable of file names (keys in self.files) to read from. If None, read from all. skip_missing: If True, skip files where the tag is missing; if False, raise if missing.

Returns:

A dictionary mapping file names to tag values.

Raises:

KeyError: If a file is not found in the collection, or if a tag is missing (when skip_missing=False). ValueError: If the tag or IFD is invalid.

Example:
>>> from tagkit.image.collection import ExifImageCollection
>>> collection = ExifImageCollection(["image1.jpg", "image2.jpg"])
>>> collection.read_tag('Make')
{'image1.jpg': 'Tagkit', 'image2.jpg': 'Tagkit'}
>>> collection.read_tag('Artist', skip_missing=True)
{}
read_tags(tag_keys: list[Union[str, int]], ifd: Optional[tagkit.core.types.IfdName] = None, format_value: bool = False, binary_format: Optional[str] = None, files: Optional[Iterable[tagkit.core.types.FilePath]] = None, skip_missing: bool = False) dict[str, dict[str, tagkit.core.types.TagValue]]

Read multiple EXIF tags from all or selected images in the collection.

Args:

tag_keys: A list of tag names or tag IDs to read. ifd: Specific IFD to use for all tags. format_value: If True, return formatted string values; if False, return raw values. binary_format: How to format binary data - ‘bytes’, ‘hex’, or ‘base64’. Only used when format_value=True. files: Iterable of file names (keys in self.files) to read from. If None, read from all. skip_missing: If True, skip missing tags; if False, only include tags that exist.

Returns:

dict[str, dict[str, Any]]: Dictionary mapping file names to dictionaries of tag names to values: {‘image1.jpg’: {‘Make’: ‘Canon’, ‘Model’: ‘EOS’}}

Raises:

KeyError: If a file is not found in the collection. ValueError: If a tag or IFD is invalid.

Example:
>>> from tagkit.image.collection import ExifImageCollection
>>> collection = ExifImageCollection(["image1.jpg", "image20.jpg"])
>>> collection.read_tags(['Make', 'Model'])
{'image1.jpg': {'Make': 'Tagkit', 'Model': 'Tagkit Camera'}, 'image20.jpg': {'Make': 'Tagkit', 'Model': 'Tagkit Camera'}}