Source code for osxphotos.exportoptions

"""Options class and results class for PhotoExporter """

from __future__ import annotations

import dataclasses
from datetime import datetime
from typing import Any, Callable, Optional

from ._constants import DEFAULT_PREVIEW_SUFFIX
from .export_db import ExportDB
from .fileutil import FileUtil
from .phototemplate import RenderOptions

# These two classes are in a separate file as classes other than PhotoExporter need to use them

__all__ = ["ExportOptions", "ExportResults"]


[docs] @dataclasses.dataclass class ExportOptions: """Options class for exporting photos with export Attributes: convert_to_jpeg (bool): if True, converts non-jpeg images to jpeg description_template (str): Optional template string that will be rendered for use as photo description download_missing: (bool, default=False): if True will attempt to export photo via applescript interaction with Photos if missing (see also use_photokit, use_photos_export) dry_run: (bool, default=False): set to True to run in "dry run" mode edited: (bool, default=False): if True will export the edited version of the photo otherwise exports the original version exiftool_flags (list of str): Optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"] exiftool: (bool, default = False): if True, will use exiftool to write metadata to export file export_as_hardlink: (bool, default=False): if True, will hardlink files instead of copying them export_db: (ExportDB): instance of a class that conforms to ExportDB with methods for getting/setting data related to exported files to compare update state face_regions: (bool, default=True): if True, will export face regions fileutil: (FileUtilABC): class that conforms to FileUtilABC with various file utilities force_update: (bool, default=False): if True, will export photo if any metadata has changed but export otherwise would not be triggered (e.g. metadata changed but not using exiftool) ignore_date_modified (bool): for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set ignore_signature (bool, default=False): ignore file signature when used with update (look only at filename) increment (bool, default=True): if True, will increment file name until a non-existant name is found if overwrite=False and increment=False, export will fail if destination file already exists jpeg_ext (str): if set, will use this value for extension on jpegs converted to jpeg with convert_to_jpeg; if not set, uses jpeg; do not include the leading "." jpeg_quality (float in range 0.0 <= jpeg_quality <= 1.0): a value of 1.0 specifies use best quality, a value of 0.0 specifies use maximum compression. keyword_template (list of str): list of template strings that will be rendered as used as keywords live_photo (bool, default=False): if True, will also export the associated .mov for live photos location (bool): if True, include location in exported metadata merge_exif_keywords (bool): if True, merged keywords found in file's exif data (requires exiftool) merge_exif_persons (bool): if True, merged persons found in file's exif data (requires exiftool) overwrite (bool, default=False): if True will overwrite files if they already exist persons (bool): if True, include persons in exported metadata preview_suffix (str): Optional string to append to end of filename for preview images preview (bool): if True, also exports preview image raw_photo (bool, default=False): if True, will also export the associated RAW photo render_options (RenderOptions): Optional osxphotos.phototemplate.RenderOptions instance to specify options for rendering templates replace_keywords (bool): if True, keyword_template replaces any keywords, otherwise it's additive rich (bool): if True, will use rich markup with verbose output export_aae (bool): if True, also exports adjustments as .AAE file sidecar_drop_ext (bool, default=False): if True, drops the photo's extension from sidecar filename (e.g. 'IMG_1234.json' instead of 'IMG_1234.JPG.json') sidecar: bit field (int): set to one or more of `SIDECAR_XMP`, `SIDECAR_JSON`, `SIDECAR_EXIFTOOL` - SIDECAR_JSON: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; includes exiftool tag group names (e.g. `exiftool -G -j`) - SIDECAR_EXIFTOOL: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; does not include exiftool tag group names (e.g. `exiftool -j`) - SIDECAR_XMP: if set will write an XMP sidecar with IPTC data sidecar filename will be dest/filename.xmp strip (bool): if True, strip whitespace from rendered templates timeout (int, default=120): timeout in seconds used with use_photos_export touch_file (bool, default=False): if True, sets file's modification time upon photo date update (bool, default=False): if True export will run in update mode, that is, it will not export the photo if the current version already exists in the destination update_errors (bool, default=False): if True photos that previously produced a warning or error will be re-exported; otherwise they will note be use_albums_as_keywords (bool, default = False): if True, will include album names in keywords when exporting metadata with exiftool or sidecar use_persons_as_keywords (bool, default = False): if True, will include person names in keywords when exporting metadata with exiftool or sidecar use_photos_export (bool, default=False): if True will attempt to export photo via applescript interaction with Photos even if not missing (see also use_photokit, download_missing) use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True verbose (callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output. tmpdir: (str, default=None): Optional directory to use for temporary files, if None (default) uses system tmp directory favorite_rating (bool): if True, set XMP:Rating=5 for favorite images and XMP:Rating=0 for non-favorites fix_orientation (bool): if True, will adjust image orientation based on exif data if necessary """ convert_to_jpeg: bool = False description_template: Optional[str] = None download_missing: bool = False dry_run: bool = False edited: bool = False exiftool_flags: Optional[list[str]] = None exiftool: bool = False export_as_hardlink: bool = False export_db: Optional[ExportDB] = None face_regions: bool = True fileutil: Optional[FileUtil] = None force_update: bool = False ignore_date_modified: bool = False ignore_signature: bool = False increment: bool = True jpeg_ext: Optional[str] = None jpeg_quality: float = 1.0 keyword_template: Optional[list[str]] = None live_photo: bool = False location: bool = True merge_exif_keywords: bool = False merge_exif_persons: bool = False overwrite: bool = False persons: bool = True preview_suffix: str = DEFAULT_PREVIEW_SUFFIX preview: bool = False raw_photo: bool = False render_options: Optional[RenderOptions] = None replace_keywords: bool = False rich: bool = False export_aae: bool = False sidecar_drop_ext: bool = False sidecar: int = 0 strip: bool = False timeout: int = 120 touch_file: bool = False update: bool = False update_errors: bool = False use_albums_as_keywords: bool = False use_persons_as_keywords: bool = False use_photokit: bool = False use_photos_export: bool = False verbose: Optional[Callable[[Any], Any]] = None tmpdir: Optional[str] = None favorite_rating: bool = False fix_orientation: bool = False def asdict(self): return dataclasses.asdict(self) @property def bit_flags(self): """Return bit flags representing options that affect export""" # currently only exiftool makes a difference return self.exiftool << 1
[docs] class ExportResults: """Results class which holds export results for export Args: converted_to_jpeg: list of files converted to jpeg deleted_directories: list of directories deleted deleted_files: list of files deleted error: list of tuples of (filename, error) for any errors generated during export exif_updated: list of files where exif data was updated with exiftool exiftool_error: list of tuples of (filename, error) for any errors generated by exiftool exiftool_warning: list of tuples of (filename, warning) for any warnings generated by exiftool exported: list of files exported exported_album: list of tuples of (file, album) for any files exported to an album metadata_changed: list of filenames that had metadata changes since last export missing: list of files that were missing missing_album: list of tuples of (file, album) for any files that were missing from an album new: list of files that were new aae_written: list of files where .AAE file was written aae_skipped: list of files where .AAE file was written sidecar_exiftool_skipped: list of files where exiftool sidecar was skipped sidecar_exiftool_written: list of files where exiftool sidecar was written sidecar_json_skipped: list of files where json sidecar was skipped sidecar_json_written: list of files where json sidecar was written sidecar_xmp_skipped: list of files where xmp sidecar was skipped sidecar_xmp_written: list of files where xmp sidecar was written sidecar_user_written: list of files where user sidecar was written sidecar_user_skipped: list of files where user sidecar was skipped sidecar_user_error: list of tuples of (filename, error) for any errors generated by user sidecar skipped: list of files that were skipped skipped_album: list of tuples of (file, album) for any files that were skipped from an album to_touch: list of files that were touched touched: list of files that were touched updated: list of files that were updated xattr_skipped: list of files where xattr was skipped xattr_written: list of files where xattr was written user_written: list of files written by user post_function user_skipped: list of files skipped by user post_function user_error: list of tuples of (filename, error) for any errors generated by user post_function Notes: Each attribute is a list of files or None if no files for that attribute. Error and warning attributes are a list of tuples of (filename, error) where filename is the file that caused the error and error is the error message. Album attributes are a list of tuples of (file, album) where file is the file exported and album is the album it was exported to. ExportResults can be added together with the += operator to combine results as the export progresses. """ # Note: __init__ docs above added in the class docstring so they are picked up by sphinx __slots__ = [ "_datetime", "converted_to_jpeg", "deleted_directories", "deleted_files", "error", "exif_updated", "exiftool_error", "exiftool_warning", "exported", "exported_album", "metadata_changed", "missing", "missing_album", "new", "aae_written", "aae_skipped", "sidecar_exiftool_skipped", "sidecar_exiftool_written", "sidecar_json_skipped", "sidecar_json_written", "sidecar_xmp_skipped", "sidecar_xmp_written", "sidecar_user_written", "sidecar_user_skipped", "sidecar_user_error", "skipped", "skipped_album", "to_touch", "touched", "updated", "xattr_skipped", "xattr_written", "user_written", "user_skipped", "user_error", ] def __init__( self, converted_to_jpeg: list[str] | None = None, deleted_directories: list[str] | None = None, deleted_files: list[str] | None = None, error: list[str] | None = None, exif_updated: list[str] | None = None, exiftool_error: list[tuple[str, str]] | None = None, exiftool_warning: list[tuple[str, str]] | None = None, exported: list[str] | None = None, exported_album: list[tuple[str, str]] | None = None, metadata_changed: list[str] | None = None, missing: list[str] | None = None, missing_album: list[tuple[str, str]] | None = None, new: list[str] | None = None, aae_written: list[str] | None = None, aae_skipped: list[str] | None = None, sidecar_exiftool_skipped: list[str] | None = None, sidecar_exiftool_written: list[str] | None = None, sidecar_json_skipped: list[str] | None = None, sidecar_json_written: list[str] | None = None, sidecar_xmp_skipped: list[str] | None = None, sidecar_xmp_written: list[str] | None = None, sidecar_user_written: list[str] | None = None, sidecar_user_skipped: list[str] | None = None, sidecar_user_error: list[tuple[str, str]] | None = None, skipped: list[str] | None = None, skipped_album: list[tuple[str, str]] | None = None, to_touch: list[str] | None = None, touched: list[str] | None = None, updated: list[str] | None = None, xattr_skipped: list[str] | None = None, xattr_written: list[str] | None = None, user_written: list[str] | None = None, user_skipped: list[str] | None = None, user_error: list[tuple[str, str]] | None = None, ): """ExportResults data class to hold results of export. See class docstring for details. """ local_vars = locals() self._datetime = datetime.now().isoformat() for attr in self.attributes: setattr(self, attr, local_vars.get(attr) or []) @property def attributes(self) -> list[str]: """Return list of attributes tracked by ExportResults""" return [attr for attr in self.__slots__ if not attr.startswith("_")] @property def datetime(self) -> str: """Return datetime when ExportResults was created""" return self._datetime
[docs] def all_files(self) -> list[str]: """return all filenames contained in results""" files = ( self.exported + self.new + self.updated + self.skipped + self.exif_updated + self.touched + self.converted_to_jpeg + self.aae_written + self.aae_skipped + self.sidecar_json_written + self.sidecar_json_skipped + self.sidecar_exiftool_written + self.sidecar_exiftool_skipped + self.sidecar_xmp_written + self.sidecar_xmp_skipped + self.sidecar_user_written + self.sidecar_user_skipped + self.missing + self.user_written + self.user_skipped ) files += [x[0] for x in self.exiftool_warning] files += [x[0] for x in self.exiftool_error] files += [x[0] for x in self.error] files += [x[0] for x in self.sidecar_user_error] files += [x[0] for x in self.user_error] return list(set(files))
def __iadd__(self, other) -> "ExportResults": if type(other) != ExportResults: raise TypeError("Can only add ExportResults to ExportResults") for attribute in self.attributes: setattr( self, attribute, getattr(self, attribute) + getattr(other, attribute) ) return self def __str__(self) -> str: return ( "ExportResults(" + f"datetime={self._datetime}, " + ", ".join([f"{attr}={getattr(self, attr)}" for attr in self.attributes]) + ")" )