2328 lines
296 KiB
HTML
2328 lines
296 KiB
HTML
<!doctype html>
|
|
<html class="no-js">
|
|
<head><meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
|
|
|
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
|
<title>osxphotos.photoexporter - osxphotos 0.51.7 documentation</title>
|
|
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
|
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
|
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
|
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
|
|
|
|
|
|
|
|
|
<style>
|
|
body {
|
|
--color-code-background: #f8f8f8;
|
|
--color-code-foreground: black;
|
|
|
|
}
|
|
@media not print {
|
|
body[data-theme="dark"] {
|
|
--color-code-background: #202020;
|
|
--color-code-foreground: #d0d0d0;
|
|
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
body:not([data-theme="light"]) {
|
|
--color-code-background: #202020;
|
|
--color-code-foreground: #d0d0d0;
|
|
|
|
}
|
|
}
|
|
}
|
|
</style></head>
|
|
<body>
|
|
|
|
<script>
|
|
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
|
</script>
|
|
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
<symbol id="svg-toc" viewBox="0 0 24 24">
|
|
<title>Contents</title>
|
|
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
|
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
|
</svg>
|
|
</symbol>
|
|
<symbol id="svg-menu" viewBox="0 0 24 24">
|
|
<title>Menu</title>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
</svg>
|
|
</symbol>
|
|
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
|
<title>Expand</title>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
|
<polyline points="9 18 15 12 9 6"></polyline>
|
|
</svg>
|
|
</symbol>
|
|
<symbol id="svg-sun" viewBox="0 0 24 24">
|
|
<title>Light mode</title>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
|
<circle cx="12" cy="12" r="5"></circle>
|
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
</svg>
|
|
</symbol>
|
|
<symbol id="svg-moon" viewBox="0 0 24 24">
|
|
<title>Dark mode</title>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
|
</svg>
|
|
</symbol>
|
|
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
|
<title>Auto light/dark mode</title>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<path d="M13 12h5" />
|
|
<path d="M13 15h4" />
|
|
<path d="M13 18h1" />
|
|
<path d="M13 9h4" />
|
|
<path d="M13 6h1" />
|
|
</svg>
|
|
</symbol>
|
|
</svg>
|
|
|
|
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
|
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
|
<label class="overlay sidebar-overlay" for="__navigation">
|
|
<div class="visually-hidden">Hide navigation sidebar</div>
|
|
</label>
|
|
<label class="overlay toc-overlay" for="__toc">
|
|
<div class="visually-hidden">Hide table of contents sidebar</div>
|
|
</label>
|
|
|
|
|
|
|
|
<div class="page">
|
|
<header class="mobile-header">
|
|
<div class="header-left">
|
|
<label class="nav-overlay-icon" for="__navigation">
|
|
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
|
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
|
</label>
|
|
</div>
|
|
<div class="header-center">
|
|
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
|
|
</div>
|
|
<div class="header-right">
|
|
<div class="theme-toggle-container theme-toggle-header">
|
|
<button class="theme-toggle">
|
|
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
</button>
|
|
</div>
|
|
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
|
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
</label>
|
|
</div>
|
|
</header>
|
|
<aside class="sidebar-drawer">
|
|
<div class="sidebar-container">
|
|
|
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
|
|
|
|
|
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
|
|
|
|
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
|
<input type="hidden" name="check_keywords" value="yes">
|
|
<input type="hidden" name="area" value="default">
|
|
</form>
|
|
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
|
|
</ul>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</aside>
|
|
<div class="main">
|
|
<div class="content">
|
|
<div class="article-container">
|
|
<a href="#" class="back-to-top muted-link">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
|
</svg>
|
|
<span>Back to top</span>
|
|
</a>
|
|
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
|
|
<button class="theme-toggle">
|
|
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
|
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|
</button>
|
|
</div>
|
|
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
|
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
|
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
|
</label>
|
|
</div>
|
|
<article role="main">
|
|
<h1>Source code for osxphotos.photoexporter</h1><div class="highlight"><pre>
|
|
<span></span><span class="sd">""" PhotoExport class to export photos</span>
|
|
<span class="sd">"""</span>
|
|
|
|
<span class="kn">import</span> <span class="nn">dataclasses</span>
|
|
<span class="kn">import</span> <span class="nn">json</span>
|
|
<span class="kn">import</span> <span class="nn">logging</span>
|
|
<span class="kn">import</span> <span class="nn">os</span>
|
|
<span class="kn">import</span> <span class="nn">pathlib</span>
|
|
<span class="kn">import</span> <span class="nn">re</span>
|
|
<span class="kn">import</span> <span class="nn">typing</span> <span class="k">as</span> <span class="nn">t</span>
|
|
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
|
|
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">asdict</span><span class="p">,</span> <span class="n">dataclass</span>
|
|
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
|
|
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
|
|
|
|
<span class="kn">import</span> <span class="nn">photoscript</span>
|
|
<span class="kn">from</span> <span class="nn">mako.template</span> <span class="kn">import</span> <span class="n">Template</span>
|
|
|
|
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="p">(</span>
|
|
<span class="n">_MAX_IPTC_KEYWORD_LEN</span><span class="p">,</span>
|
|
<span class="n">_OSXPHOTOS_NONE_SENTINEL</span><span class="p">,</span>
|
|
<span class="n">_TEMPLATE_DIR</span><span class="p">,</span>
|
|
<span class="n">_UNKNOWN_PERSON</span><span class="p">,</span>
|
|
<span class="n">_XMP_TEMPLATE_NAME</span><span class="p">,</span>
|
|
<span class="n">_XMP_TEMPLATE_NAME_BETA</span><span class="p">,</span>
|
|
<span class="n">DEFAULT_PREVIEW_SUFFIX</span><span class="p">,</span>
|
|
<span class="n">LIVE_VIDEO_EXTENSIONS</span><span class="p">,</span>
|
|
<span class="n">SIDECAR_EXIFTOOL</span><span class="p">,</span>
|
|
<span class="n">SIDECAR_JSON</span><span class="p">,</span>
|
|
<span class="n">SIDECAR_XMP</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="kn">from</span> <span class="nn">._version</span> <span class="kn">import</span> <span class="n">__version__</span>
|
|
<span class="kn">from</span> <span class="nn">.datetime_utils</span> <span class="kn">import</span> <span class="n">datetime_tz_to_utc</span>
|
|
<span class="kn">from</span> <span class="nn">.exiftool</span> <span class="kn">import</span> <span class="n">ExifTool</span><span class="p">,</span> <span class="n">exiftool_can_write</span>
|
|
<span class="kn">from</span> <span class="nn">.export_db</span> <span class="kn">import</span> <span class="n">ExportDB</span><span class="p">,</span> <span class="n">ExportDBTemp</span>
|
|
<span class="kn">from</span> <span class="nn">.fileutil</span> <span class="kn">import</span> <span class="n">FileUtil</span>
|
|
<span class="kn">from</span> <span class="nn">.photokit</span> <span class="kn">import</span> <span class="p">(</span>
|
|
<span class="n">PHOTOS_VERSION_CURRENT</span><span class="p">,</span>
|
|
<span class="n">PHOTOS_VERSION_ORIGINAL</span><span class="p">,</span>
|
|
<span class="n">PHOTOS_VERSION_UNADJUSTED</span><span class="p">,</span>
|
|
<span class="n">PhotoKitFetchFailed</span><span class="p">,</span>
|
|
<span class="n">PhotoLibrary</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="kn">from</span> <span class="nn">.phototemplate</span> <span class="kn">import</span> <span class="n">RenderOptions</span>
|
|
<span class="kn">from</span> <span class="nn">.rich_utils</span> <span class="kn">import</span> <span class="n">add_rich_markup_tag</span>
|
|
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span>
|
|
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="p">(</span>
|
|
<span class="n">hexdigest</span><span class="p">,</span>
|
|
<span class="n">increment_filename</span><span class="p">,</span>
|
|
<span class="n">increment_filename_with_count</span><span class="p">,</span>
|
|
<span class="n">lineno</span><span class="p">,</span>
|
|
<span class="n">list_directory</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
|
|
<span class="s2">"ExportError"</span><span class="p">,</span>
|
|
<span class="s2">"ExportOptions"</span><span class="p">,</span>
|
|
<span class="s2">"ExportResults"</span><span class="p">,</span>
|
|
<span class="s2">"PhotoExporter"</span><span class="p">,</span>
|
|
<span class="s2">"rename_jpeg_files"</span><span class="p">,</span>
|
|
<span class="p">]</span>
|
|
|
|
<span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">TYPE_CHECKING</span><span class="p">:</span>
|
|
<span class="kn">from</span> <span class="nn">.photoinfo</span> <span class="kn">import</span> <span class="n">PhotoInfo</span>
|
|
|
|
<span class="c1"># retry if download_missing/use_photos_export fails the first time (which sometimes it does)</span>
|
|
<span class="n">MAX_PHOTOSCRIPT_RETRIES</span> <span class="o">=</span> <span class="mi">3</span>
|
|
|
|
<span class="c1"># return values for _should_update_photo</span>
|
|
<span class="k">class</span> <span class="nc">ShouldUpdate</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
|
<span class="n">NOT_IN_DATABASE</span> <span class="o">=</span> <span class="mi">1</span>
|
|
<span class="n">HARDLINK_DIFFERENT_FILES</span> <span class="o">=</span> <span class="mi">2</span>
|
|
<span class="n">NOT_HARDLINK_SAME_FILES</span> <span class="o">=</span> <span class="mi">3</span>
|
|
<span class="n">DEST_SIG_DIFFERENT</span> <span class="o">=</span> <span class="mi">4</span>
|
|
<span class="n">EXPORT_OPTIONS_DIFFERENT</span> <span class="o">=</span> <span class="mi">5</span>
|
|
<span class="n">EXIFTOOL_DIFFERENT</span> <span class="o">=</span> <span class="mi">6</span>
|
|
<span class="n">EDITED_SIG_DIFFERENT</span> <span class="o">=</span> <span class="mi">7</span>
|
|
<span class="n">DIGEST_DIFFERENT</span> <span class="o">=</span> <span class="mi">8</span>
|
|
|
|
|
|
<span class="k">class</span> <span class="nc">ExportError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
|
<span class="sd">"""error during export"""</span>
|
|
|
|
<span class="k">pass</span>
|
|
|
|
|
|
<div class="viewcode-block" id="ExportOptions"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportOptions">[docs]</a><span class="nd">@dataclass</span>
|
|
<span class="k">class</span> <span class="nc">ExportOptions</span><span class="p">:</span>
|
|
<span class="sd">"""Options class for exporting photos with export</span>
|
|
|
|
<span class="sd"> Attributes:</span>
|
|
<span class="sd"> convert_to_jpeg (bool): if True, converts non-jpeg images to jpeg</span>
|
|
<span class="sd"> description_template (str): t.Optional template string that will be rendered for use as photo description</span>
|
|
<span class="sd"> 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)</span>
|
|
<span class="sd"> dry_run: (bool, default=False): set to True to run in "dry run" mode</span>
|
|
<span class="sd"> edited: (bool, default=False): if True will export the edited version of the photo otherwise exports the original version</span>
|
|
<span class="sd"> exiftool_flags (list of str): t.Optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"]</span>
|
|
<span class="sd"> exiftool: (bool, default = False): if True, will use exiftool to write metadata to export file</span>
|
|
<span class="sd"> export_as_hardlink: (bool, default=False): if True, will hardlink files instead of copying them</span>
|
|
<span class="sd"> 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</span>
|
|
<span class="sd"> face_regions: (bool, default=True): if True, will export face regions</span>
|
|
<span class="sd"> fileutil: (FileUtilABC): class that conforms to FileUtilABC with various file utilities</span>
|
|
<span class="sd"> 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)</span>
|
|
<span class="sd"> ignore_date_modified (bool): for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set</span>
|
|
<span class="sd"> ignore_signature (bool, default=False): ignore file signature when used with update (look only at filename)</span>
|
|
<span class="sd"> 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</span>
|
|
<span class="sd"> 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 "."</span>
|
|
<span class="sd"> 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.</span>
|
|
<span class="sd"> keyword_template (list of str): list of template strings that will be rendered as used as keywords</span>
|
|
<span class="sd"> live_photo (bool, default=False): if True, will also export the associated .mov for live photos</span>
|
|
<span class="sd"> location (bool): if True, include location in exported metadata</span>
|
|
<span class="sd"> merge_exif_keywords (bool): if True, merged keywords found in file's exif data (requires exiftool)</span>
|
|
<span class="sd"> merge_exif_persons (bool): if True, merged persons found in file's exif data (requires exiftool)</span>
|
|
<span class="sd"> overwrite (bool, default=False): if True will overwrite files if they already exist</span>
|
|
<span class="sd"> persons (bool): if True, include persons in exported metadata</span>
|
|
<span class="sd"> preview_suffix (str): t.Optional string to append to end of filename for preview images</span>
|
|
<span class="sd"> preview (bool): if True, also exports preview image</span>
|
|
<span class="sd"> raw_photo (bool, default=False): if True, will also export the associated RAW photo</span>
|
|
<span class="sd"> render_options (RenderOptions): t.Optional osxphotos.phototemplate.RenderOptions instance to specify options for rendering templates</span>
|
|
<span class="sd"> replace_keywords (bool): if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
|
<span class="sd"> rich (bool): if True, will use rich markup with verbose output</span>
|
|
<span class="sd"> 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')</span>
|
|
<span class="sd"> sidecar: bit field (int): set to one or more of `SIDECAR_XMP`, `SIDECAR_JSON`, `SIDECAR_EXIFTOOL`</span>
|
|
<span class="sd"> - SIDECAR_JSON: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json;</span>
|
|
<span class="sd"> includes exiftool tag group names (e.g. `exiftool -G -j`)</span>
|
|
<span class="sd"> - SIDECAR_EXIFTOOL: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json;</span>
|
|
<span class="sd"> does not include exiftool tag group names (e.g. `exiftool -j`)</span>
|
|
<span class="sd"> - SIDECAR_XMP: if set will write an XMP sidecar with IPTC data sidecar filename will be dest/filename.xmp</span>
|
|
<span class="sd"> strip (bool): if True, strip whitespace from rendered templates</span>
|
|
<span class="sd"> timeout (int, default=120): timeout in seconds used with use_photos_export</span>
|
|
<span class="sd"> touch_file (bool, default=False): if True, sets file's modification time upon photo date</span>
|
|
<span class="sd"> 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</span>
|
|
<span class="sd"> use_albums_as_keywords (bool, default = False): if True, will include album names in keywords when exporting metadata with exiftool or sidecar</span>
|
|
<span class="sd"> use_persons_as_keywords (bool, default = False): if True, will include person names in keywords when exporting metadata with exiftool or sidecar</span>
|
|
<span class="sd"> 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)</span>
|
|
<span class="sd"> use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True</span>
|
|
<span class="sd"> verbose (callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output.</span>
|
|
<span class="sd"> tmpdir: (str, default=None): Optional directory to use for temporary files, if None (default) uses system tmp directory</span>
|
|
<span class="sd"> favorite_rating (bool): if True, set XMP:Rating=5 for favorite images and XMP:Rating=0 for non-favorites</span>
|
|
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">convert_to_jpeg</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">description_template</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">download_missing</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">dry_run</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">edited</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">exiftool_flags</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">exiftool</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">export_as_hardlink</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">export_db</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportDB</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">face_regions</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">fileutil</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">FileUtil</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">force_update</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">ignore_date_modified</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">ignore_signature</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">increment</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">jpeg_ext</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">jpeg_quality</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">1.0</span>
|
|
<span class="n">keyword_template</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">live_photo</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">location</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">merge_exif_keywords</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">merge_exif_persons</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">overwrite</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">persons</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">preview_suffix</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">DEFAULT_PREVIEW_SUFFIX</span>
|
|
<span class="n">preview</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">raw_photo</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">render_options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">RenderOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">replace_keywords</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">rich</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">sidecar_drop_ext</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">sidecar</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
|
|
<span class="n">strip</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">timeout</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">120</span>
|
|
<span class="n">touch_file</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">update</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">use_albums_as_keywords</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">use_persons_as_keywords</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">use_photokit</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">use_photos_export</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">verbose</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">Callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">tmpdir</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="n">favorite_rating</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
|
|
|
|
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
|
|
|
<span class="nd">@property</span>
|
|
<span class="k">def</span> <span class="nf">bit_flags</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="sd">"""Return bit flags representing options that affect export"""</span>
|
|
<span class="c1"># currently only exiftool makes a difference</span>
|
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool</span> <span class="o"><<</span> <span class="mi">1</span></div>
|
|
|
|
|
|
<span class="k">class</span> <span class="nc">StagedFiles</span><span class="p">:</span>
|
|
<span class="sd">"""Represents files staged for export"""</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">original</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">original_live</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">edited</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">edited_live</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">preview</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">raw</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">error</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">original</span> <span class="o">=</span> <span class="n">original</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">original_live</span> <span class="o">=</span> <span class="n">original_live</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">edited</span> <span class="o">=</span> <span class="n">edited</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">edited_live</span> <span class="o">=</span> <span class="n">edited_live</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">preview</span> <span class="o">=</span> <span class="n">preview</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="n">raw</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span> <span class="ow">or</span> <span class="p">[]</span>
|
|
|
|
<span class="c1"># TODO: bursts?</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__ior__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">original</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">original</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">original</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">original_live</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_live</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">original_live</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">edited</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">edited</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">edited</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">edited_live</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">edited_live</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">edited_live</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">preview</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">preview</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">preview</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span> <span class="ow">or</span> <span class="n">other</span><span class="o">.</span><span class="n">raw</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">error</span>
|
|
<span class="k">return</span> <span class="bp">self</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">())</span>
|
|
|
|
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="k">return</span> <span class="p">{</span>
|
|
<span class="s2">"original"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original</span><span class="p">,</span>
|
|
<span class="s2">"original_live"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_live</span><span class="p">,</span>
|
|
<span class="s2">"edited"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">edited</span><span class="p">,</span>
|
|
<span class="s2">"edited_live"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">edited_live</span><span class="p">,</span>
|
|
<span class="s2">"preview"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">preview</span><span class="p">,</span>
|
|
<span class="s2">"raw"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">,</span>
|
|
<span class="s2">"error"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">,</span>
|
|
<span class="p">}</span>
|
|
|
|
|
|
<div class="viewcode-block" id="ExportResults"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults">[docs]</a><span class="k">class</span> <span class="nc">ExportResults</span><span class="p">:</span>
|
|
<span class="sd">"""Results class which holds export results for export"""</span>
|
|
|
|
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">[</span>
|
|
<span class="s2">"_datetime"</span><span class="p">,</span>
|
|
<span class="s2">"converted_to_jpeg"</span><span class="p">,</span>
|
|
<span class="s2">"deleted_directories"</span><span class="p">,</span>
|
|
<span class="s2">"deleted_files"</span><span class="p">,</span>
|
|
<span class="s2">"error"</span><span class="p">,</span>
|
|
<span class="s2">"exif_updated"</span><span class="p">,</span>
|
|
<span class="s2">"exiftool_error"</span><span class="p">,</span>
|
|
<span class="s2">"exiftool_warning"</span><span class="p">,</span>
|
|
<span class="s2">"exported"</span><span class="p">,</span>
|
|
<span class="s2">"exported_album"</span><span class="p">,</span>
|
|
<span class="s2">"metadata_changed"</span><span class="p">,</span>
|
|
<span class="s2">"missing"</span><span class="p">,</span>
|
|
<span class="s2">"missing_album"</span><span class="p">,</span>
|
|
<span class="s2">"new"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_exiftool_skipped"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_exiftool_written"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_json_skipped"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_json_written"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_xmp_skipped"</span><span class="p">,</span>
|
|
<span class="s2">"sidecar_xmp_written"</span><span class="p">,</span>
|
|
<span class="s2">"skipped"</span><span class="p">,</span>
|
|
<span class="s2">"skipped_album"</span><span class="p">,</span>
|
|
<span class="s2">"to_touch"</span><span class="p">,</span>
|
|
<span class="s2">"touched"</span><span class="p">,</span>
|
|
<span class="s2">"updated"</span><span class="p">,</span>
|
|
<span class="s2">"xattr_skipped"</span><span class="p">,</span>
|
|
<span class="s2">"xattr_written"</span><span class="p">,</span>
|
|
<span class="p">]</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">converted_to_jpeg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">deleted_directories</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">deleted_files</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">exif_updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">exiftool_warning</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">exported</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">exported_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">metadata_changed</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">missing</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">new</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_json_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">to_touch</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">touched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
|
|
<span class="n">local_vars</span> <span class="o">=</span> <span class="nb">locals</span><span class="p">()</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
|
<span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
|
|
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">local_vars</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">attr</span><span class="p">)</span> <span class="ow">or</span> <span class="p">[])</span>
|
|
|
|
<span class="nd">@property</span>
|
|
<span class="k">def</span> <span class="nf">attributes</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
|
<span class="sd">"""Return list of attributes tracked by ExportResults"""</span>
|
|
<span class="k">return</span> <span class="p">[</span><span class="n">attr</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__slots__</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">attr</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"_"</span><span class="p">)]</span>
|
|
|
|
<span class="nd">@property</span>
|
|
<span class="k">def</span> <span class="nf">datetime</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
|
<span class="sd">"""Return datetime when ExportResults was created"""</span>
|
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span>
|
|
|
|
<div class="viewcode-block" id="ExportResults.all_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults.all_files">[docs]</a> <span class="k">def</span> <span class="nf">all_files</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
|
<span class="sd">"""return all filenames contained in results"""</span>
|
|
<span class="n">files</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">new</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">updated</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">skipped</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">touched</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">converted_to_jpeg</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span>
|
|
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">missing</span>
|
|
<span class="p">)</span>
|
|
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="p">]</span>
|
|
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span><span class="p">]</span>
|
|
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">]</span>
|
|
|
|
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">files</span><span class="p">))</span></div>
|
|
|
|
<span class="k">def</span> <span class="fm">__iadd__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span> <span class="o">-></span> <span class="s2">"ExportResults"</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">other</span><span class="p">)</span> <span class="o">!=</span> <span class="n">ExportResults</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"Can only add ExportResults to ExportResults"</span><span class="p">)</span>
|
|
|
|
<span class="k">for</span> <span class="n">attribute</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
|
|
<span class="nb">setattr</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span> <span class="n">attribute</span><span class="p">,</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attribute</span><span class="p">)</span> <span class="o">+</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">attribute</span><span class="p">)</span>
|
|
<span class="p">)</span>
|
|
<span class="k">return</span> <span class="bp">self</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
|
<span class="k">return</span> <span class="p">(</span>
|
|
<span class="s2">"ExportResults("</span>
|
|
<span class="o">+</span> <span class="sa">f</span><span class="s2">"datetime=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span><span class="si">}</span><span class="s2">, "</span>
|
|
<span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">attr</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">])</span>
|
|
<span class="o">+</span> <span class="s2">")"</span>
|
|
<span class="p">)</span></div>
|
|
|
|
|
|
<div class="viewcode-block" id="PhotoExporter"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter">[docs]</a><span class="k">class</span> <span class="nc">PhotoExporter</span><span class="p">:</span>
|
|
<span class="sd">"""Export a photo"""</span>
|
|
|
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="s2">"PhotoInfo"</span><span class="p">,</span> <span class="n">tmpdir</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span> <span class="o">=</span> <span class="n">photo</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span> <span class="o">=</span> <span class="n">RenderOptions</span><span class="p">()</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_verbose</span>
|
|
|
|
<span class="c1"># define functions for adding markup</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"filepath"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_filename</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"filename"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"uuid"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_num</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"num"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
|
|
|
<span class="c1"># temp directory for staging downloaded missing files</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">fileutil</span> <span class="o">=</span> <span class="n">FileUtil</span>
|
|
|
|
<div class="viewcode-block" id="PhotoExporter.export"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.export">[docs]</a> <span class="k">def</span> <span class="nf">export</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">,</span>
|
|
<span class="n">filename</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
|
<span class="sd">"""Export photo</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> dest: must be valid destination path or exception raised</span>
|
|
<span class="sd"> filename: (optional): name of exported picture; if not provided, will use current filename</span>
|
|
<span class="sd"> **NOTE**: if provided, user must ensure file extension (suffix) is correct.</span>
|
|
<span class="sd"> For example, if photo is .CR2 file, edited image may be .jpeg.</span>
|
|
<span class="sd"> If you provide an extension different than what the actual file is,</span>
|
|
<span class="sd"> will export the photo using the incorrect file extension (unless use_photos_export is true,</span>
|
|
<span class="sd"> in which case export will use the extension provided by Photos upon export.</span>
|
|
<span class="sd"> e.g. to get the extension of the edited photo,</span>
|
|
<span class="sd"> reference PhotoInfo.path_edited</span>
|
|
<span class="sd"> options (`ExportOptions`): t.Optional ExportOptions instance</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> ExportResults instance</span>
|
|
|
|
<span class="sd"> Note:</span>
|
|
<span class="sd"> To use dry run mode, you must set options.dry_run=True and also pass in memory version of export_db,</span>
|
|
<span class="sd"> and no-op fileutil (e.g. `ExportDBInMemory` and `FileUtilNoOp`) in options.export_db and options.fileutil respectively</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">ExportOptions</span><span class="p">()</span>
|
|
|
|
<span class="c1"># temp dir must be initialized before any of the methods called by export() are called</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_init_temp_dir</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
|
<span class="k">if</span> <span class="n">verbose</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">callable</span><span class="p">(</span><span class="n">verbose</span><span class="p">):</span>
|
|
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"verbose must be callable"</span><span class="p">)</span>
|
|
|
|
<span class="c1"># define functions for adding markup</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"filepath"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">rich</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_filename</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"filename"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">rich</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"uuid"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">rich</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_num</span> <span class="o">=</span> <span class="n">add_rich_markup_tag</span><span class="p">(</span><span class="s2">"num"</span><span class="p">,</span> <span class="n">rich</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">rich</span><span class="p">)</span>
|
|
|
|
<span class="c1"># can't use export_as_hardlink with download_missing, use_photos_export as can't hardlink the temporary files downloaded</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">download_missing</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
|
|
<span class="s2">"Cannot use export_as_hardlink with download_missing or use_photos_export"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># when called from export(), won't get an export_db, so use temp version</span>
|
|
<span class="n">options</span><span class="o">.</span><span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span> <span class="ow">or</span> <span class="n">ExportDBTemp</span><span class="p">()</span>
|
|
|
|
<span class="c1"># ensure there's a FileUtil class to use</span>
|
|
<span class="n">options</span><span class="o">.</span><span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span> <span class="ow">or</span> <span class="n">FileUtil</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span>
|
|
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">render_options</span> <span class="ow">or</span> <span class="n">RenderOptions</span><span class="p">()</span>
|
|
|
|
<span class="c1"># export_original, and export_edited are just used for clarity in the code</span>
|
|
<span class="n">export_original</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span>
|
|
<span class="n">export_edited</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span>
|
|
<span class="k">if</span> <span class="n">export_edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
|
|
<span class="s2">"Photo does not have adjustments, cannot export edited version"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># verify destination is a valid path</span>
|
|
<span class="k">if</span> <span class="n">dest</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"dest must not be None"</span><span class="p">)</span>
|
|
<span class="k">elif</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">dest</span><span class="p">):</span>
|
|
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span><span class="s2">"Invalid path passed to export"</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">export_edited</span><span class="p">:</span>
|
|
<span class="n">filename</span> <span class="o">=</span> <span class="n">filename</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_edited_filename</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">filename</span> <span class="o">=</span> <span class="n">filename</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="o">/</span> <span class="n">filename</span>
|
|
|
|
<span class="c1"># Is there something to convert with convert_to_jpeg?</span>
|
|
<span class="n">dest</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_should_convert_to_jpeg</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="c1"># stage files for export by finding path in local library or downloading from iCloud as appropriate</span>
|
|
<span class="n">staged_files</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photos_for_export</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="n">src</span> <span class="o">=</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">edited</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">original</span>
|
|
|
|
<span class="c1"># get the right destination path depending on options.update, etc.</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_dest_path</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="o">.</span><span class="n">filepath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="n">all_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
|
|
|
<span class="k">if</span> <span class="n">src</span><span class="p">:</span>
|
|
<span class="c1"># export the dest file</span>
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
|
|
<span class="n">src</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipping missing </span><span class="si">{</span><span class="s1">'edited'</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s1">'original'</span><span class="si">}</span><span class="s2"> photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
|
|
<span class="c1"># copy live photo associated .mov if requested</span>
|
|
<span class="k">if</span> <span class="n">export_original</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
|
|
<span class="n">live_name</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">.mov"</span>
|
|
<span class="k">if</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">original_live</span><span class="p">:</span>
|
|
<span class="n">src_live</span> <span class="o">=</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">original_live</span>
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
|
|
<span class="n">src_live</span><span class="p">,</span>
|
|
<span class="n">live_name</span><span class="p">,</span>
|
|
<span class="c1"># don't try to convert the live photo</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">convert_to_jpeg</span><span class="o">=</span><span class="kc">False</span><span class="p">),</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipping missing live photo for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">live_name</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">export_edited</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
|
|
<span class="n">live_name</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">.mov"</span>
|
|
<span class="k">if</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">edited_live</span><span class="p">:</span>
|
|
<span class="n">src_live</span> <span class="o">=</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">edited_live</span>
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
|
|
<span class="n">src_live</span><span class="p">,</span>
|
|
<span class="n">live_name</span><span class="p">,</span>
|
|
<span class="c1"># don't try to convert the live photo</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">convert_to_jpeg</span><span class="o">=</span><span class="kc">False</span><span class="p">),</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipping missing edited live photo for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">live_name</span><span class="p">)</span>
|
|
|
|
<span class="c1"># copy associated RAW image if requested</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">raw</span><span class="p">:</span>
|
|
<span class="n">raw_path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">staged_files</span><span class="o">.</span><span class="n">raw</span><span class="p">)</span>
|
|
<span class="n">raw_ext</span> <span class="o">=</span> <span class="n">raw_path</span><span class="o">.</span><span class="n">suffix</span>
|
|
<span class="n">raw_name</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">raw_ext</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
|
|
<span class="n">raw_path</span><span class="p">,</span>
|
|
<span class="n">raw_name</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="c1"># guess at most likely raw name</span>
|
|
<span class="n">raw_ext</span> <span class="o">=</span> <span class="n">get_preferred_uti_extension</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_raw</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">"raw"</span>
|
|
<span class="n">raw_name</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">raw_ext</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">raw_name</span><span class="p">)</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipping missing raw photo for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># copy preview image if requested</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">preview</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">preview</span><span class="p">:</span>
|
|
<span class="c1"># Photos keeps multiple different derivatives and path_derivatives returns list of them</span>
|
|
<span class="c1"># first derivative is the largest so export that one</span>
|
|
<span class="n">preview_path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">staged_files</span><span class="o">.</span><span class="n">preview</span><span class="p">)</span>
|
|
<span class="n">preview_ext</span> <span class="o">=</span> <span class="n">preview_path</span><span class="o">.</span><span class="n">suffix</span>
|
|
<span class="n">preview_name</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">options</span><span class="o">.</span><span class="n">preview_suffix</span><span class="si">}{</span><span class="n">preview_ext</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="c1"># if original is missing, the filename won't have been incremented so</span>
|
|
<span class="c1"># need to check here to make sure there aren't duplicate preview files in</span>
|
|
<span class="c1"># the export directory</span>
|
|
<span class="n">preview_name</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="n">preview_name</span>
|
|
<span class="k">if</span> <span class="nb">any</span><span class="p">([</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">])</span>
|
|
<span class="k">else</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">preview_name</span><span class="p">))</span>
|
|
<span class="p">)</span>
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo</span><span class="p">(</span>
|
|
<span class="n">preview_path</span><span class="p">,</span>
|
|
<span class="n">preview_name</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="c1"># don't know what actual preview suffix would be but most likely jpeg</span>
|
|
<span class="n">preview_name</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">options</span><span class="o">.</span><span class="n">preview_suffix</span><span class="si">}</span><span class="s2">.jpeg"</span>
|
|
<span class="n">all_results</span><span class="o">.</span><span class="n">missing</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">preview_name</span><span class="p">)</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipping missing preview photo for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="n">all_results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_write_sidecar_files</span><span class="p">(</span><span class="n">dest</span><span class="o">=</span><span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="k">return</span> <span class="n">all_results</span></div>
|
|
|
|
<span class="k">def</span> <span class="nf">_init_temp_dir</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">):</span>
|
|
<span class="sd">"""Initialize (if necessary) the object's temporary directory.</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> options: ExportOptions object</span>
|
|
<span class="sd"> """</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="k">return</span>
|
|
|
|
<span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span> <span class="ow">or</span> <span class="n">FileUtil</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">tmpdir</span><span class="p">(</span><span class="n">prefix</span><span class="o">=</span><span class="s2">"osxphotos_export_"</span><span class="p">,</span> <span class="nb">dir</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">tmpdir</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
|
<span class="k">return</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_touch_files</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span> <span class="n">touch_files</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">List</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
|
<span class="sd">"""touch file date/time to match photo creation date/time; only touches files if needed"""</span>
|
|
<span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span>
|
|
<span class="n">touch_results</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">for</span> <span class="n">touch_file</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">touch_files</span><span class="p">):</span>
|
|
<span class="n">ts</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">timestamp</span><span class="p">())</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">stat</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">touch_file</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">stat</span><span class="o">.</span><span class="n">st_mtime</span> <span class="o">!=</span> <span class="n">ts</span><span class="p">:</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">utime</span><span class="p">(</span><span class="n">touch_file</span><span class="p">,</span> <span class="p">(</span><span class="n">ts</span><span class="p">,</span> <span class="n">ts</span><span class="p">))</span>
|
|
<span class="n">touch_results</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">touch_file</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">FileNotFoundError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="c1"># ignore errors if in dry_run as file may not be present</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="n">e</span> <span class="kn">from</span> <span class="nn">e</span>
|
|
<span class="k">return</span> <span class="n">ExportResults</span><span class="p">(</span><span class="n">touched</span><span class="o">=</span><span class="n">touch_results</span><span class="p">)</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_get_edited_filename</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">original_filename</span><span class="p">):</span>
|
|
<span class="sd">"""Return the filename for the exported edited photo</span>
|
|
<span class="sd"> (used when filename isn't provided in call to export)"""</span>
|
|
<span class="c1"># need to get the right extension for edited file</span>
|
|
<span class="n">original_filename</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">original_filename</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_edited</span><span class="p">:</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_edited</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">uti</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="n">get_preferred_uti_extension</span><span class="p">(</span><span class="n">uti</span><span class="p">)</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="s2">"."</span> <span class="o">+</span> <span class="n">ext</span>
|
|
<span class="n">edited_filename</span> <span class="o">=</span> <span class="n">original_filename</span><span class="o">.</span><span class="n">stem</span> <span class="o">+</span> <span class="s2">"_edited"</span> <span class="o">+</span> <span class="n">ext</span>
|
|
<span class="k">return</span> <span class="n">edited_filename</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_get_dest_path</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">:</span>
|
|
<span class="sd">"""If destination exists find match in ExportDB, on disk, or add (1), (2), and so on to filename to get a valid destination</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> dest (str): destination path</span>
|
|
<span class="sd"> options (ExportOptions): Export options</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> new dest path (pathlib.Path)</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="c1"># if overwrite==False and #increment==False, export should fail if file exists</span>
|
|
<span class="k">if</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
|
|
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">]</span>
|
|
<span class="p">):</span>
|
|
<span class="k">raise</span> <span class="ne">FileExistsError</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"destination exists (</span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">); overwrite=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="si">}</span><span class="s2">, increment=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># if overwrite, we don't care if the file exists or not</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">:</span>
|
|
<span class="k">return</span> <span class="n">dest</span>
|
|
|
|
<span class="c1"># if not update or overwrite, check to see if file exists and if so, add (1), (2), etc</span>
|
|
<span class="c1"># until we find one that works</span>
|
|
<span class="c1"># Photos checks the stem and adds (1), (2), etc which avoids collision with sidecars</span>
|
|
<span class="c1"># e.g. exporting sidecar for file1.png and file1.jpeg</span>
|
|
<span class="c1"># if file1.png exists and exporting file1.jpeg,</span>
|
|
<span class="c1"># dest will be file1 (1).jpeg even though file1.jpeg doesn't exist to prevent sidecar collision</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">increment</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span>
|
|
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">]</span>
|
|
<span class="p">):</span>
|
|
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">))</span>
|
|
|
|
<span class="c1"># if update and file exists, need to check to see if it's the right file by checking export db</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span>
|
|
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
|
|
<span class="n">dest_uuid</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
|
<span class="c1"># destination doesn't exist in export db and doesn't exist on disk</span>
|
|
<span class="c1"># so we can just use it</span>
|
|
<span class="k">return</span> <span class="n">dest</span>
|
|
|
|
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
|
|
<span class="c1"># destination is the right file</span>
|
|
<span class="k">return</span> <span class="n">dest</span>
|
|
|
|
<span class="c1"># either dest_uuid is wrong or file exists and there's no associated UUID, so find a name that matches</span>
|
|
<span class="c1"># or create a new name if no match</span>
|
|
<span class="c1"># find files that match "dest_name (*.ext" (e.g. "dest_name (1).jpg", "dest_name (2).jpg)", ...)</span>
|
|
<span class="c1"># first, find all matching files in export db and see if there's a match</span>
|
|
<span class="k">if</span> <span class="n">dest_target</span> <span class="o">:=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_target_for_file</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
|
|
<span class="c1"># there's a match so use that</span>
|
|
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest_target</span><span class="p">)</span>
|
|
|
|
<span class="c1"># no match so need to create a new name</span>
|
|
<span class="c1"># increment the destination file until we find one that doesn't exist and doesn't match another uuid in the database</span>
|
|
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
|
|
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
|
|
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
|
|
<span class="k">while</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
|
|
<span class="c1"># fail safe...I can't think of a case that gets here</span>
|
|
<span class="k">return</span> <span class="n">dest</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_should_update_photo</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span> <span class="n">src</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">t</span><span class="o">.</span><span class="n">Literal</span><span class="p">[</span><span class="kc">True</span><span class="p">,</span> <span class="kc">False</span><span class="p">]:</span>
|
|
<span class="sd">"""Return True if photo should be updated, else False"""</span>
|
|
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
|
|
<span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span>
|
|
|
|
<span class="n">file_record</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_file_record</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">file_record</span><span class="p">:</span>
|
|
<span class="c1"># photo doesn't exist in database, should update</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">NOT_IN_DATABASE</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
|
|
<span class="c1"># different files, should update</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">HARDLINK_DIFFERENT_FILES</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="n">dest</span><span class="o">.</span><span class="n">samefile</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
|
|
<span class="c1"># same file but not exporting as hardlink, should update</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">NOT_HARDLINK_SAME_FILES</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_signature</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">cmp_file_sig</span><span class="p">(</span>
|
|
<span class="n">dest</span><span class="p">,</span> <span class="n">file_record</span><span class="o">.</span><span class="n">dest_sig</span>
|
|
<span class="p">):</span>
|
|
<span class="c1"># destination file doesn't match what was last exported</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">DEST_SIG_DIFFERENT</span>
|
|
|
|
<span class="k">if</span> <span class="n">file_record</span><span class="o">.</span><span class="n">export_options</span> <span class="o">!=</span> <span class="n">options</span><span class="o">.</span><span class="n">bit_flags</span><span class="p">:</span>
|
|
<span class="c1"># exporting with different set of options (e.g. exiftool), should update</span>
|
|
<span class="c1"># need to check this before exiftool in case exiftool options are different</span>
|
|
<span class="c1"># and export database is missing; this will always be True if database is missing</span>
|
|
<span class="c1"># as it'll be None and bit_flags will be an int</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EXPORT_OPTIONS_DIFFERENT</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
|
<span class="n">current_exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">current_exifdata</span> <span class="o">!=</span> <span class="n">file_record</span><span class="o">.</span><span class="n">exifdata</span>
|
|
<span class="c1"># if using exiftool, don't need to continue checking edited below</span>
|
|
<span class="c1"># as exiftool will be used to update edited file</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EXIFTOOL_DIFFERENT</span> <span class="k">if</span> <span class="n">rv</span> <span class="k">else</span> <span class="kc">False</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">cmp_file_sig</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">file_record</span><span class="o">.</span><span class="n">src_sig</span><span class="p">):</span>
|
|
<span class="c1"># edited file in Photos doesn't match what was last exported</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EDITED_SIG_DIFFERENT</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span>
|
|
<span class="n">current_digest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
|
|
<span class="k">if</span> <span class="n">current_digest</span> <span class="o">!=</span> <span class="n">file_record</span><span class="o">.</span><span class="n">digest</span><span class="p">:</span>
|
|
<span class="c1"># metadata in Photos changed, force update</span>
|
|
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">DIGEST_DIFFERENT</span>
|
|
|
|
<span class="c1"># photo should not be updated</span>
|
|
<span class="k">return</span> <span class="kc">False</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_stage_photos_for_export</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">)</span> <span class="o">-></span> <span class="n">StagedFiles</span><span class="p">:</span>
|
|
<span class="sd">"""Stages photos for export</span>
|
|
|
|
<span class="sd"> If photo is present on disk in the library, uses path to the photo on disk.</span>
|
|
<span class="sd"> If photo is missing and download_missing is true, downloads the photo from iCloud to temporary location.</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">staged</span> <span class="o">=</span> <span class="n">StagedFiles</span><span class="p">()</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_photos_export</span><span class="p">:</span>
|
|
<span class="c1"># use Photos AppleScript or PhotoKit to do the export</span>
|
|
<span class="k">return</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_photokit</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_photokit</span>
|
|
<span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_applescript</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span><span class="p">:</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_raw</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">:</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">preview</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span><span class="p">:</span>
|
|
<span class="c1"># original file</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span><span class="p">:</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">original</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">original_live</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_live_photo</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span><span class="p">:</span>
|
|
<span class="c1"># edited file</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">edited</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_edited</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
|
|
<span class="n">staged</span><span class="o">.</span><span class="n">edited_live</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_edited_live_photo</span>
|
|
|
|
<span class="c1"># download any missing files</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">download_missing</span><span class="p">:</span>
|
|
<span class="n">live_photo</span> <span class="o">=</span> <span class="n">staged</span><span class="o">.</span><span class="n">edited_live</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged</span><span class="o">.</span><span class="n">original_live</span>
|
|
<span class="n">missing_options</span> <span class="o">=</span> <span class="n">ExportOptions</span><span class="p">(</span>
|
|
<span class="n">edited</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">edited</span><span class="p">,</span>
|
|
<span class="n">preview</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">preview</span><span class="p">,</span>
|
|
<span class="n">raw_photo</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">staged</span><span class="o">.</span><span class="n">raw</span><span class="p">,</span>
|
|
<span class="n">live_photo</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">live_photo</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_photokit</span><span class="p">:</span>
|
|
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_photokit</span><span class="p">(</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">missing_staged</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_stage_photo_for_export_with_applescript</span><span class="p">(</span>
|
|
<span class="n">options</span><span class="o">=</span><span class="n">missing_options</span>
|
|
<span class="p">)</span>
|
|
<span class="n">staged</span> <span class="o">|=</span> <span class="n">missing_staged</span>
|
|
<span class="k">return</span> <span class="n">staged</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_stage_photo_for_export_with_photokit</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">StagedFiles</span><span class="p">:</span>
|
|
<span class="sd">"""Stage a photo for export with photokit to a temporary directory"""</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Edited version requested but photo has no adjustments"</span><span class="p">)</span>
|
|
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span>
|
|
|
|
<span class="c1"># export live_photo .mov file?</span>
|
|
<span class="n">live_photo</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">)</span>
|
|
|
|
<span class="n">overwrite</span> <span class="o">=</span> <span class="nb">any</span><span class="p">([</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">])</span>
|
|
|
|
<span class="c1"># figure out which photo version to request</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
|
|
<span class="c1"># shared photos (in shared albums) show up as not having adjustments (not edited)</span>
|
|
<span class="c1"># but Photos is unable to export the "original" as only a jpeg copy is shared in iCloud</span>
|
|
<span class="c1"># so tell Photos to export the current version in this case</span>
|
|
<span class="n">photos_version</span> <span class="o">=</span> <span class="n">PHOTOS_VERSION_CURRENT</span>
|
|
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span><span class="p">:</span>
|
|
<span class="c1"># PhotoKit always returns the raw photo of raw+jpeg pair for PHOTOS_VERSION_ORIGINAL even if JPEG is the original</span>
|
|
<span class="n">photos_version</span> <span class="o">=</span> <span class="n">PHOTOS_VERSION_UNADJUSTED</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">photos_version</span> <span class="o">=</span> <span class="n">PHOTOS_VERSION_ORIGINAL</span>
|
|
|
|
<span class="n">uti</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span>
|
|
<span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span>
|
|
<span class="p">)</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="n">get_preferred_uti_extension</span><span class="p">(</span><span class="n">uti</span><span class="p">)</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">"</span>
|
|
|
|
<span class="n">photolib</span> <span class="o">=</span> <span class="n">PhotoLibrary</span><span class="p">()</span>
|
|
<span class="n">results</span> <span class="o">=</span> <span class="n">StagedFiles</span><span class="p">()</span>
|
|
<span class="n">photo</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">photo</span> <span class="o">=</span> <span class="n">photolib</span><span class="o">.</span><span class="n">fetch_uuid</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="n">PhotoKitFetchFailed</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="c1"># if failed to find UUID, might be a burst photo</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"burstUUID"</span><span class="p">]:</span>
|
|
<span class="n">bursts</span> <span class="o">=</span> <span class="n">photolib</span><span class="o">.</span><span class="n">fetch_burst_uuid</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"burstUUID"</span><span class="p">],</span> <span class="nb">all</span><span class="o">=</span><span class="kc">True</span>
|
|
<span class="p">)</span>
|
|
<span class="c1"># PhotoKit UUIDs may contain "/L0/001" so only look at beginning</span>
|
|
<span class="n">photo</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">bursts</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)]</span>
|
|
<span class="n">photo</span> <span class="o">=</span> <span class="n">photo</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">photo</span> <span class="k">else</span> <span class="kc">None</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">photo</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
|
<span class="p">(</span>
|
|
<span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">),</span>
|
|
<span class="sa">f</span><span class="s2">"PhotoKitFetchFailed exception exporting photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<span class="c1"># now export the requested version of the photo</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">exported</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">export</span><span class="p">(</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
|
|
<span class="n">version</span><span class="o">=</span><span class="n">photos_version</span><span class="p">,</span>
|
|
<span class="n">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
|
|
<span class="n">video</span><span class="o">=</span><span class="n">live_photo</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="s2">"edited"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s2">"original"</span>
|
|
<span class="nb">setattr</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">results_attr</span><span class="p">,</span> <span class="n">exported</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
|
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
|
|
<span class="k">for</span> <span class="n">exported_file</span> <span class="ow">in</span> <span class="n">exported</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">exported_file</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".mov"</span><span class="p">):</span>
|
|
<span class="c1"># live photo</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="s2">"edited_live"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s2">"original_live"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="s2">"edited"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s2">"original"</span>
|
|
<span class="nb">setattr</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">results_attr</span><span class="p">,</span> <span class="n">exported_file</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">),</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">))</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span><span class="p">:</span>
|
|
<span class="c1"># also request the raw photo</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">exported</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">export</span><span class="p">(</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
|
|
<span class="n">version</span><span class="o">=</span><span class="n">photos_version</span><span class="p">,</span>
|
|
<span class="n">raw</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
|
<span class="n">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
|
|
<span class="n">video</span><span class="o">=</span><span class="n">live_photo</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">exported</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="n">exported</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">),</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">))</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">preview</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_stage_photo_for_export_with_applescript</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">StagedFiles</span><span class="p">:</span>
|
|
<span class="sd">"""Stage a photo for export with AppleScript to a temporary directory</span>
|
|
|
|
<span class="sd"> Note: If exporting an edited live photo, the associated live video will not be exported.</span>
|
|
<span class="sd"> This is a limitation of the Photos AppleScript interface and Photos behaves the same way."""</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Edited version requested but photo has no adjustments"</span><span class="p">)</span>
|
|
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">))</span>
|
|
|
|
<span class="c1"># export live_photo .mov file?</span>
|
|
<span class="n">live_photo</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">live_photo</span><span class="p">)</span>
|
|
<span class="n">overwrite</span> <span class="o">=</span> <span class="nb">any</span><span class="p">([</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">])</span>
|
|
<span class="n">edited_version</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">shared</span><span class="p">)</span>
|
|
<span class="c1"># shared photos (in shared albums) show up as not having adjustments (not edited)</span>
|
|
<span class="c1"># but Photos is unable to export the "original" as only a jpeg copy is shared in iCloud</span>
|
|
<span class="c1"># so tell Photos to export the current version in this case</span>
|
|
<span class="n">uti</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_edited</span>
|
|
<span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span>
|
|
<span class="p">)</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="n">get_preferred_uti_extension</span><span class="p">(</span><span class="n">uti</span><span class="p">)</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">"</span>
|
|
|
|
<span class="n">results</span> <span class="o">=</span> <span class="n">StagedFiles</span><span class="p">()</span>
|
|
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">exported</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_export_photo_uuid_applescript</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span>
|
|
<span class="n">filestem</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="p">,</span>
|
|
<span class="n">original</span><span class="o">=</span><span class="ow">not</span> <span class="n">edited_version</span><span class="p">,</span>
|
|
<span class="n">edited</span><span class="o">=</span><span class="n">edited_version</span><span class="p">,</span>
|
|
<span class="n">live_photo</span><span class="o">=</span><span class="n">live_photo</span><span class="p">,</span>
|
|
<span class="n">timeout</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">timeout</span><span class="p">,</span>
|
|
<span class="n">burst</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
|
|
<span class="n">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="k">except</span> <span class="n">ExportError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">),</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">))</span>
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="s2">"edited"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s2">"original"</span>
|
|
<span class="nb">setattr</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">results_attr</span><span class="p">,</span> <span class="n">exported</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
|
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">exported</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
|
|
<span class="c1"># could be live or raw+jpeg</span>
|
|
<span class="k">for</span> <span class="n">exported_file</span> <span class="ow">in</span> <span class="n">exported</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">exported_file</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".mov"</span><span class="p">):</span>
|
|
<span class="c1"># live photo</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="s2">"edited_live"</span>
|
|
<span class="k">if</span> <span class="n">live_photo</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span>
|
|
<span class="k">else</span> <span class="s2">"original_live"</span>
|
|
<span class="k">if</span> <span class="n">live_photo</span>
|
|
<span class="k">else</span> <span class="kc">None</span>
|
|
<span class="p">)</span>
|
|
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">has_raw</span> <span class="ow">and</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
|
<span class="n">exported_file</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
|
<span class="p">)</span><span class="o">.</span><span class="n">suffix</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span>
|
|
<span class="s2">".jpg"</span><span class="p">,</span>
|
|
<span class="s2">".jpeg"</span><span class="p">,</span>
|
|
<span class="s2">".heic"</span><span class="p">,</span>
|
|
<span class="p">]:</span>
|
|
<span class="c1"># assume raw photo if not a common non-raw image format</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="s2">"raw"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">raw_photo</span> <span class="k">else</span> <span class="kc">None</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">results_attr</span> <span class="o">=</span> <span class="s2">"edited"</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="s2">"original"</span>
|
|
<span class="k">if</span> <span class="n">results_attr</span><span class="p">:</span>
|
|
<span class="nb">setattr</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">results_attr</span><span class="p">,</span> <span class="n">exported_file</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">preview</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">preview</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_should_convert_to_jpeg</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">t</span><span class="o">.</span><span class="n">Tuple</span><span class="p">[</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">ExportOptions</span><span class="p">]:</span>
|
|
<span class="sd">"""Determine if a file really should be converted to jpeg or not</span>
|
|
<span class="sd"> and return the new destination and ExportOptions instance with the convert_to_jpeg flag set appropriately</span>
|
|
<span class="sd"> """</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">convert_to_jpeg</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span><span class="p">):</span>
|
|
<span class="c1"># nothing to convert</span>
|
|
<span class="k">return</span> <span class="n">dest</span><span class="p">,</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">convert_to_jpeg</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
|
|
|
<span class="n">convert_to_jpeg</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">ext</span> <span class="o">=</span> <span class="s2">"."</span> <span class="o">+</span> <span class="n">options</span><span class="o">.</span><span class="n">jpeg_ext</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">jpeg_ext</span> <span class="k">else</span> <span class="s2">".jpeg"</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti_original</span> <span class="o">!=</span> <span class="s2">"public.jpeg"</span><span class="p">:</span>
|
|
<span class="c1"># not a jpeg but will convert to jpeg upon export so fix file extension</span>
|
|
<span class="n">convert_to_jpeg</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">ext</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uti</span> <span class="o">!=</span> <span class="s2">"public.jpeg"</span><span class="p">:</span>
|
|
<span class="c1"># in Big Sur+, edited HEICs are HEIC</span>
|
|
<span class="n">convert_to_jpeg</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">ext</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="k">return</span> <span class="n">dest</span><span class="p">,</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">convert_to_jpeg</span><span class="o">=</span><span class="n">convert_to_jpeg</span><span class="p">)</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_is_temp_file</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
|
<span class="sd">"""Returns True if file is in the PhotosExporter temp directory otherwise False"""</span>
|
|
<span class="n">filepath</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">filepath</span><span class="o">.</span><span class="n">parent</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_copy_to_temp_file</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
|
<span class="sd">"""Copies filepath to a temp file preserving access and modification times"""</span>
|
|
<span class="n">filepath</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">/</span> <span class="n">filepath</span><span class="o">.</span><span class="n">name</span>
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">fileutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">dest</span><span class="p">)</span>
|
|
<span class="n">stat</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">fileutil</span><span class="o">.</span><span class="n">utime</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="p">(</span><span class="n">stat</span><span class="o">.</span><span class="n">st_atime</span><span class="p">,</span> <span class="n">stat</span><span class="o">.</span><span class="n">st_mtime</span><span class="p">))</span>
|
|
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_export_photo</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">src</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="sd">"""Helper function for export()</span>
|
|
<span class="sd"> Does the actual copy or hardlink taking the appropriate</span>
|
|
<span class="sd"> action depending on update, overwrite, export_as_hardlink</span>
|
|
<span class="sd"> Assumes destination is the right destination (e.g. UUID matches)</span>
|
|
<span class="sd"> Sets UUID and JSON info for exported file using set_uuid_for_file, set_info_for_uuid</span>
|
|
<span class="sd"> Expects that src is a temporary file (as set by _stage_photos_for_export) and</span>
|
|
<span class="sd"> may modify the src (e.g. for convert_to_jpeg or exiftool)</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> src (str): src path</span>
|
|
<span class="sd"> dest (pathlib.Path): dest path</span>
|
|
<span class="sd"> options (ExportOptions): options for export</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> ExportResults</span>
|
|
|
|
<span class="sd"> Raises:</span>
|
|
<span class="sd"> ValueError if export_as_hardlink and convert_to_jpeg both True</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="n">options</span><span class="o">.</span><span class="n">convert_to_jpeg</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
|
|
<span class="s2">"export_as_hardlink and convert_to_jpeg cannot both be True"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_temp_file</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"export_as_hardlink cannot be used with temp files"</span><span class="p">)</span>
|
|
|
|
<span class="n">exported_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">update_updated_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">update_new_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">update_skipped_files</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># skip files that are already up to date</span>
|
|
<span class="n">converted_to_jpeg_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
|
|
|
<span class="n">dest_str</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="n">dest_exists</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span>
|
|
|
|
<span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span>
|
|
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span> <span class="c1"># updating</span>
|
|
<span class="k">if</span> <span class="n">dest_exists</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_should_update_photo</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">):</span>
|
|
<span class="n">update_updated_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">update_skipped_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="c1"># update, destination doesn't exist (new file)</span>
|
|
<span class="n">update_new_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="c1"># not update, export the file</span>
|
|
<span class="n">exported_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
|
|
|
|
<span class="n">export_files</span> <span class="o">=</span> <span class="n">update_new_files</span> <span class="o">+</span> <span class="n">update_updated_files</span> <span class="o">+</span> <span class="n">exported_files</span>
|
|
<span class="k">for</span> <span class="n">export_dest</span> <span class="ow">in</span> <span class="n">export_files</span><span class="p">:</span>
|
|
<span class="c1"># set src_sig before any modifications by convert_to_jpeg or exiftool</span>
|
|
<span class="n">export_record</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">create_or_get_file_record</span><span class="p">(</span>
|
|
<span class="n">export_dest</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span>
|
|
<span class="p">)</span>
|
|
<span class="n">export_record</span><span class="o">.</span><span class="n">src_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">dest_exists</span> <span class="ow">and</span> <span class="nb">any</span><span class="p">(</span>
|
|
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">]</span>
|
|
<span class="p">):</span>
|
|
<span class="c1"># need to remove the destination first</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="n">ExportError</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Error removing file </span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> ((</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span><span class="p">:</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">hardlink</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="n">ExportError</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Error hardlinking </span><span class="si">{</span><span class="n">src</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">convert_to_jpeg</span><span class="p">:</span>
|
|
<span class="c1"># use convert_to_jpeg to export the file</span>
|
|
<span class="c1"># convert to a temp file before copying</span>
|
|
<span class="n">tmp_file</span> <span class="o">=</span> <span class="n">increment_filename</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span>
|
|
<span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_converted_to_jpeg.jpeg"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">convert_to_jpeg</span><span class="p">(</span>
|
|
<span class="n">src</span><span class="p">,</span> <span class="n">tmp_file</span><span class="p">,</span> <span class="n">compression_quality</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">jpeg_quality</span>
|
|
<span class="p">)</span>
|
|
<span class="n">src</span> <span class="o">=</span> <span class="n">tmp_file</span>
|
|
<span class="n">converted_to_jpeg_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
|
<span class="c1"># if exiftool, write the metadata</span>
|
|
<span class="c1"># need to copy the file to a temp file before writing metadata</span>
|
|
<span class="n">src</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
|
|
<span class="n">tmp_file</span> <span class="o">=</span> <span class="n">increment_filename</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">src</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_exiftool</span><span class="si">{</span><span class="n">src</span><span class="o">.</span><span class="n">suffix</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">tmp_file</span><span class="p">)</span>
|
|
<span class="c1"># point src to the tmp_file so that the original source is not modified</span>
|
|
<span class="c1"># and the export grabs the new file</span>
|
|
<span class="n">src</span> <span class="o">=</span> <span class="n">tmp_file</span>
|
|
<span class="n">exif_results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
|
<span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest_str</span><span class="p">)</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Exported </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filename</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">dest_str</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="n">ExportError</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Error copying file </span><span class="si">{</span><span class="n">src</span><span class="si">}</span><span class="s2"> to </span><span class="si">{</span><span class="n">dest_str</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
|
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
|
|
|
<span class="n">results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">(</span>
|
|
<span class="n">converted_to_jpeg</span><span class="o">=</span><span class="n">converted_to_jpeg_files</span><span class="p">,</span>
|
|
<span class="n">error</span><span class="o">=</span><span class="n">exif_results</span><span class="o">.</span><span class="n">error</span><span class="p">,</span>
|
|
<span class="n">exif_updated</span><span class="o">=</span><span class="n">exif_results</span><span class="o">.</span><span class="n">exif_updated</span><span class="p">,</span>
|
|
<span class="n">exiftool_error</span><span class="o">=</span><span class="n">exif_results</span><span class="o">.</span><span class="n">exiftool_error</span><span class="p">,</span>
|
|
<span class="n">exiftool_warning</span><span class="o">=</span><span class="n">exif_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="p">,</span>
|
|
<span class="n">exported</span><span class="o">=</span><span class="n">exported_files</span> <span class="o">+</span> <span class="n">update_new_files</span> <span class="o">+</span> <span class="n">update_updated_files</span><span class="p">,</span>
|
|
<span class="n">new</span><span class="o">=</span><span class="n">update_new_files</span><span class="p">,</span>
|
|
<span class="n">skipped</span><span class="o">=</span><span class="n">update_skipped_files</span><span class="p">,</span>
|
|
<span class="n">updated</span><span class="o">=</span><span class="n">update_updated_files</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># touch files if needed</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">touch_file</span><span class="p">:</span>
|
|
<span class="n">results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_touch_files</span><span class="p">(</span>
|
|
<span class="n">exported_files</span>
|
|
<span class="o">+</span> <span class="n">update_new_files</span>
|
|
<span class="o">+</span> <span class="n">update_updated_files</span>
|
|
<span class="o">+</span> <span class="n">update_skipped_files</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># set data in the database</span>
|
|
<span class="k">with</span> <span class="n">export_db</span><span class="o">.</span><span class="n">create_or_get_file_record</span><span class="p">(</span><span class="n">dest_str</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span> <span class="k">as</span> <span class="n">rec</span><span class="p">:</span>
|
|
<span class="n">photoinfo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
|
|
<span class="n">rec</span><span class="o">.</span><span class="n">photoinfo</span> <span class="o">=</span> <span class="n">photoinfo</span>
|
|
<span class="n">rec</span><span class="o">.</span><span class="n">export_options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">bit_flags</span>
|
|
<span class="c1"># don't set src_sig as that is set above before any modifications by convert_to_jpeg or exiftool</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_signature</span><span class="p">:</span>
|
|
<span class="n">rec</span><span class="o">.</span><span class="n">dest_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
|
<span class="n">rec</span><span class="o">.</span><span class="n">exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span> <span class="o">!=</span> <span class="n">rec</span><span class="o">.</span><span class="n">digest</span><span class="p">:</span>
|
|
<span class="n">results</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">=</span> <span class="p">[</span><span class="n">dest_str</span><span class="p">]</span>
|
|
<span class="n">rec</span><span class="o">.</span><span class="n">digest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
|
|
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_export_photo_uuid_applescript</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
|
<span class="n">filestem</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">original</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
|
<span class="n">edited</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
|
<span class="n">live_photo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
|
<span class="n">timeout</span><span class="o">=</span><span class="mi">120</span><span class="p">,</span>
|
|
<span class="n">burst</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
|
<span class="n">dry_run</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
|
<span class="n">overwrite</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="sd">"""Export photo to dest path using applescript to control Photos</span>
|
|
<span class="sd"> If photo is a live photo, exports both the photo and associated .mov file</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> uuid: UUID of photo to export</span>
|
|
<span class="sd"> dest: destination path to export to</span>
|
|
<span class="sd"> filestem: (string) if provided, exported filename will be named stem.ext</span>
|
|
<span class="sd"> where ext is extension of the file exported by photos (e.g. .jpeg, .mov, etc)</span>
|
|
<span class="sd"> If not provided, file will be named with whatever name Photos uses</span>
|
|
<span class="sd"> If filestem.ext exists, it wil be overwritten</span>
|
|
<span class="sd"> original: (boolean) if True, export original image; default = True</span>
|
|
<span class="sd"> edited: (boolean) if True, export edited photo; default = False</span>
|
|
<span class="sd"> If photo not edited and edited=True, will still export the original image</span>
|
|
<span class="sd"> caller must verify image has been edited</span>
|
|
<span class="sd"> *Note*: must be called with either edited or original but not both,</span>
|
|
<span class="sd"> will raise error if called with both edited and original = True</span>
|
|
<span class="sd"> live_photo: (boolean) if True, export associated .mov live photo; default = False</span>
|
|
<span class="sd"> timeout: timeout value in seconds; export will fail if applescript run time exceeds timeout</span>
|
|
<span class="sd"> burst: (boolean) set to True if file is a burst image to avoid Photos export error</span>
|
|
<span class="sd"> dry_run: (boolean) set to True to run in "dry run" mode which will download file but not actually copy to destination</span>
|
|
|
|
<span class="sd"> Returns: list of paths to exported file(s) or None if export failed</span>
|
|
|
|
<span class="sd"> Raises: ExportError if error during export</span>
|
|
|
|
<span class="sd"> Note: For Live Photos, if edited=True, will export a jpeg but not the movie, even if photo</span>
|
|
<span class="sd"> has not been edited. This is due to how Photos Applescript interface works.</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"dest </span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2"> must be a directory"</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">original</span> <span class="o">^</span> <span class="n">edited</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"edited or original must be True but not both"</span><span class="p">)</span>
|
|
|
|
<span class="c1"># export to a subdirectory of tmpdir</span>
|
|
<span class="n">tmpdir</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fileutil</span><span class="o">.</span><span class="n">tmpdir</span><span class="p">(</span>
|
|
<span class="s2">"osxphotos_applescript_export_"</span><span class="p">,</span> <span class="nb">dir</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_temp_dir_path</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="n">exported_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">filename</span> <span class="o">=</span> <span class="kc">None</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="c1"># I've seen intermittent failures with the PhotoScript export so retry if</span>
|
|
<span class="c1"># export doesn't return anything</span>
|
|
<span class="n">retries</span> <span class="o">=</span> <span class="mi">0</span>
|
|
<span class="k">while</span> <span class="ow">not</span> <span class="n">exported_files</span> <span class="ow">and</span> <span class="n">retries</span> <span class="o"><</span> <span class="n">MAX_PHOTOSCRIPT_RETRIES</span><span class="p">:</span>
|
|
<span class="n">photo</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">uuid</span><span class="p">)</span>
|
|
<span class="n">filename</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">filename</span>
|
|
<span class="n">exported_files</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">export</span><span class="p">(</span>
|
|
<span class="n">tmpdir</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">original</span><span class="o">=</span><span class="n">original</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span>
|
|
<span class="p">)</span>
|
|
<span class="n">retries</span> <span class="o">+=</span> <span class="mi">1</span>
|
|
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="n">ExportError</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">exported_files</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">filename</span><span class="p">:</span>
|
|
<span class="c1"># nothing got exported</span>
|
|
<span class="k">raise</span> <span class="n">ExportError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not export photo </span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">lineno</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span><span class="p">)</span>
|
|
<span class="c1"># need to find actual filename as sometimes Photos renames JPG to jpeg on export</span>
|
|
<span class="c1"># may be more than one file exported (e.g. if Live Photo, Photos exports both .jpeg and .mov)</span>
|
|
<span class="c1"># TemporaryDirectory will cleanup on return</span>
|
|
<span class="n">filename_stem</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span>
|
|
<span class="n">exported_paths</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">for</span> <span class="n">fname</span> <span class="ow">in</span> <span class="n">exported_files</span><span class="p">:</span>
|
|
<span class="n">path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">tmpdir</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="o">/</span> <span class="n">fname</span>
|
|
<span class="k">if</span> <span class="p">(</span>
|
|
<span class="nb">len</span><span class="p">(</span><span class="n">exported_files</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span>
|
|
<span class="ow">and</span> <span class="ow">not</span> <span class="n">live_photo</span>
|
|
<span class="ow">and</span> <span class="n">path</span><span class="o">.</span><span class="n">suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s2">".mov"</span>
|
|
<span class="p">):</span>
|
|
<span class="c1"># it's the .mov part of live photo but not requested, so don't export</span>
|
|
<span class="k">continue</span>
|
|
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">exported_files</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">burst</span> <span class="ow">and</span> <span class="n">path</span><span class="o">.</span><span class="n">stem</span> <span class="o">!=</span> <span class="n">filename_stem</span><span class="p">:</span>
|
|
<span class="c1"># skip any burst photo that's not the one we asked for</span>
|
|
<span class="k">continue</span>
|
|
<span class="k">if</span> <span class="n">filestem</span><span class="p">:</span>
|
|
<span class="c1"># rename the file based on filestem, keeping original extension</span>
|
|
<span class="n">dest_new</span> <span class="o">=</span> <span class="n">dest</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">filestem</span><span class="si">}{</span><span class="n">path</span><span class="o">.</span><span class="n">suffix</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="c1"># use the name Photos provided</span>
|
|
<span class="n">dest_new</span> <span class="o">=</span> <span class="n">dest</span> <span class="o">/</span> <span class="n">path</span><span class="o">.</span><span class="n">name</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">dry_run</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">overwrite</span> <span class="ow">and</span> <span class="n">dest_new</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
|
<span class="n">FileUtil</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">dest_new</span><span class="p">)</span>
|
|
<span class="n">FileUtil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">),</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest_new</span><span class="p">))</span>
|
|
<span class="n">exported_paths</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">dest_new</span><span class="p">))</span>
|
|
<span class="k">return</span> <span class="n">exported_paths</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_write_sidecar_files</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
|
<span class="sd">"""Write sidecar files for the photo."""</span>
|
|
|
|
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
|
|
<span class="n">fileutil</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fileutil</span>
|
|
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
|
|
|
<span class="c1"># export metadata</span>
|
|
<span class="n">sidecars</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_json_files_skipped</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_json_files_written</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_exiftool_files_skipped</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_exiftool_files_written</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_xmp_files_skipped</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">sidecar_xmp_files_written</span> <span class="o">=</span> <span class="p">[]</span>
|
|
|
|
<span class="n">dest_suffix</span> <span class="o">=</span> <span class="s2">""</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">sidecar_drop_ext</span> <span class="k">else</span> <span class="n">dest</span><span class="o">.</span><span class="n">suffix</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">sidecar</span> <span class="o">&</span> <span class="n">SIDECAR_JSON</span><span class="p">:</span>
|
|
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
|
<span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
|
<span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span>
|
|
<span class="n">sidecar_str</span><span class="p">,</span>
|
|
<span class="n">sidecar_json_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_json_files_skipped</span><span class="p">,</span>
|
|
<span class="s2">"JSON"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">sidecar</span> <span class="o">&</span> <span class="n">SIDECAR_EXIFTOOL</span><span class="p">:</span>
|
|
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
|
<span class="n">tag_groups</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
|
<span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span>
|
|
<span class="n">sidecar_str</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_files_skipped</span><span class="p">,</span>
|
|
<span class="s2">"exiftool"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">sidecar</span> <span class="o">&</span> <span class="n">SIDECAR_XMP</span><span class="p">:</span>
|
|
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.xmp"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_xmp_sidecar</span><span class="p">(</span>
|
|
<span class="n">extension</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">suffix</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">if</span> <span class="n">dest</span><span class="o">.</span><span class="n">suffix</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
|
<span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span>
|
|
<span class="n">sidecar_str</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_files_skipped</span><span class="p">,</span>
|
|
<span class="s2">"XMP"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">for</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">sidecars</span><span class="p">:</span>
|
|
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
|
<span class="n">files_written</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
|
|
<span class="n">files_skipped</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
|
|
<span class="n">sidecar_type</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
|
|
|
|
<span class="n">sidecar_digest</span> <span class="o">=</span> <span class="n">hexdigest</span><span class="p">(</span><span class="n">sidecar_str</span><span class="p">)</span>
|
|
<span class="n">sidecar_record</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">create_or_get_file_record</span><span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span>
|
|
<span class="p">)</span>
|
|
<span class="n">write_sidecar</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="ow">not</span> <span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">)</span>
|
|
<span class="ow">or</span> <span class="p">(</span>
|
|
<span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">)</span>
|
|
<span class="ow">and</span> <span class="ow">not</span> <span class="n">sidecar_filename</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span>
|
|
<span class="p">)</span>
|
|
<span class="ow">or</span> <span class="p">(</span>
|
|
<span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">)</span>
|
|
<span class="ow">and</span> <span class="p">(</span><span class="n">sidecar_digest</span> <span class="o">!=</span> <span class="n">sidecar_record</span><span class="o">.</span><span class="n">digest</span><span class="p">)</span>
|
|
<span class="ow">or</span> <span class="ow">not</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">cmp_file_sig</span><span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span> <span class="n">sidecar_record</span><span class="o">.</span><span class="n">dest_sig</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">write_sidecar</span><span class="p">:</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Writing </span><span class="si">{</span><span class="n">sidecar_type</span><span class="si">}</span><span class="s2"> sidecar </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">files_written</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">))</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span><span class="p">:</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_write_sidecar</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">,</span> <span class="n">sidecar_str</span><span class="p">)</span>
|
|
<span class="n">sidecar_record</span><span class="o">.</span><span class="n">digest</span> <span class="o">=</span> <span class="n">sidecar_digest</span>
|
|
<span class="n">sidecar_record</span><span class="o">.</span><span class="n">dest_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Skipped up to date </span><span class="si">{</span><span class="n">sidecar_type</span><span class="si">}</span><span class="s2"> sidecar </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">files_skipped</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">))</span>
|
|
|
|
<span class="n">results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">(</span>
|
|
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="n">sidecar_json_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_json_skipped</span><span class="o">=</span><span class="n">sidecar_json_files_skipped</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="n">sidecar_exiftool_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="n">sidecar_exiftool_files_skipped</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="n">sidecar_xmp_files_written</span><span class="p">,</span>
|
|
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="n">sidecar_xmp_files_skipped</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">touch_file</span><span class="p">:</span>
|
|
<span class="n">all_sidecars</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="n">sidecar_json_files_written</span>
|
|
<span class="o">+</span> <span class="n">sidecar_exiftool_files_written</span>
|
|
<span class="o">+</span> <span class="n">sidecar_xmp_files_written</span>
|
|
<span class="o">+</span> <span class="n">sidecar_json_files_skipped</span>
|
|
<span class="o">+</span> <span class="n">sidecar_exiftool_files_skipped</span>
|
|
<span class="o">+</span> <span class="n">sidecar_xmp_files_skipped</span>
|
|
<span class="p">)</span>
|
|
<span class="n">results</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_touch_files</span><span class="p">(</span><span class="n">all_sidecars</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="c1"># update destination signatures in database</span>
|
|
<span class="k">for</span> <span class="n">sidecar_filename</span> <span class="ow">in</span> <span class="n">all_sidecars</span><span class="p">:</span>
|
|
<span class="n">sidecar_record</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">create_or_get_file_record</span><span class="p">(</span>
|
|
<span class="n">sidecar_filename</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span>
|
|
<span class="p">)</span>
|
|
<span class="n">sidecar_record</span><span class="o">.</span><span class="n">dest_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">sidecar_filename</span><span class="p">)</span>
|
|
|
|
<span class="k">return</span> <span class="n">results</span>
|
|
|
|
<div class="viewcode-block" id="PhotoExporter.write_exiftool_metadata_to_file"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file">[docs]</a> <span class="k">def</span> <span class="nf">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">src</span><span class="p">,</span>
|
|
<span class="n">dest</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
|
<span class="sd">"""Write exif metadata to src file using exiftool</span>
|
|
|
|
<span class="sd"> Caution: This method modifies *src*, not *dest*,</span>
|
|
<span class="sd"> so src must be a copy of the original file if you don't want the source modified;</span>
|
|
<span class="sd"> it also does not write to dest (dest is the intended destination for purposes of</span>
|
|
<span class="sd"> referencing the export database. This allows the exiftool update to be done on the</span>
|
|
<span class="sd"> local machine prior to being copied to the export destination which may be on a</span>
|
|
<span class="sd"> network drive or other slower external storage)."""</span>
|
|
|
|
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
|
<span class="n">exiftool_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
|
|
|
<span class="c1"># don't try to write if unsupported file type for exiftool</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">exiftool_can_write</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">splitext</span><span class="p">(</span><span class="n">src</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]):</span>
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
|
<span class="p">(</span>
|
|
<span class="n">dest</span><span class="p">,</span>
|
|
<span class="sa">f</span><span class="s2">"Unsupported file type for exiftool, skipping exiftool for </span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
<span class="c1"># set file signature so the file doesn't get re-exported with --update</span>
|
|
<span class="k">return</span> <span class="n">exiftool_results</span>
|
|
|
|
<span class="c1"># determine if we need to write the exif metadata</span>
|
|
<span class="c1"># if we are not updating, we always write</span>
|
|
<span class="c1"># else, need to check the database to determine if we need to write</span>
|
|
<span class="n">verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Writing metadata with exiftool for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">dry_run</span><span class="p">:</span>
|
|
<span class="n">warning_</span><span class="p">,</span> <span class="n">error_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_write_exif_data</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">warning_</span><span class="p">:</span>
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">warning_</span><span class="p">))</span>
|
|
<span class="k">if</span> <span class="n">error_</span><span class="p">:</span>
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exiftool_error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">error_</span><span class="p">))</span>
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">dest</span><span class="p">,</span> <span class="n">error_</span><span class="p">))</span>
|
|
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exif_updated</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">to_touch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">exiftool_results</span></div>
|
|
|
|
<span class="k">def</span> <span class="nf">_should_run_exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
|
<span class="sd">"""Return True if exiftool should be run to update metadata"""</span>
|
|
<span class="n">run_exiftool</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span>
|
|
<span class="n">files_are_different</span> <span class="o">=</span> <span class="kc">False</span>
|
|
<span class="n">exif_record</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span><span class="o">.</span><span class="n">get_file_record</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
|
<span class="n">old_data</span> <span class="o">=</span> <span class="n">exif_record</span><span class="o">.</span><span class="n">exifdata</span> <span class="k">if</span> <span class="n">exif_record</span> <span class="k">else</span> <span class="kc">None</span>
|
|
<span class="k">if</span> <span class="n">old_data</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="n">old_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">old_data</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="n">current_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">))</span>
|
|
<span class="n">current_data</span> <span class="o">=</span> <span class="n">current_data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="k">if</span> <span class="n">old_data</span> <span class="o">!=</span> <span class="n">current_data</span><span class="p">:</span>
|
|
<span class="n">files_are_different</span> <span class="o">=</span> <span class="kc">True</span>
|
|
|
|
<span class="k">if</span> <span class="n">old_data</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">files_are_different</span><span class="p">:</span>
|
|
<span class="c1"># didn't have old data, assume we need to write it</span>
|
|
<span class="c1"># or files were different</span>
|
|
<span class="n">run_exiftool</span> <span class="o">=</span> <span class="kc">True</span>
|
|
<span class="k">return</span> <span class="n">run_exiftool</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_write_exif_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">):</span>
|
|
<span class="sd">"""write exif data to image file at filepath</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> filepath: full path to the image file</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> (warning, error) of warning and error strings if exiftool produces warnings or errors</span>
|
|
<span class="sd"> """</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">filepath</span><span class="p">):</span>
|
|
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not find file </span><span class="si">{</span><span class="n">filepath</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
|
<span class="n">exif_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_dict</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="k">with</span> <span class="n">ExifTool</span><span class="p">(</span>
|
|
<span class="n">filepath</span><span class="p">,</span>
|
|
<span class="n">flags</span><span class="o">=</span><span class="n">options</span><span class="o">.</span><span class="n">exiftool_flags</span><span class="p">,</span>
|
|
<span class="n">exiftool</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_exiftool_path</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="k">as</span> <span class="n">exiftool</span><span class="p">:</span>
|
|
<span class="k">for</span> <span class="n">exiftag</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">exif_info</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
|
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
|
|
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">val</span><span class="p">:</span>
|
|
<span class="n">exiftool</span><span class="o">.</span><span class="n">setvalue</span><span class="p">(</span><span class="n">exiftag</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">exiftool</span><span class="o">.</span><span class="n">setvalue</span><span class="p">(</span><span class="n">exiftag</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">exiftool</span><span class="o">.</span><span class="n">warning</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">.</span><span class="n">error</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_exiftool_dict</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">filename</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="sd">"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.</span>
|
|
<span class="sd"> Does not include all the EXIF fields as those are likely already in the image.</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> options (ExportOptions): options for export</span>
|
|
<span class="sd"> filename (str): name of source image file (without path); if not None, exiftool JSON signature will be included; if None, signature will not be included</span>
|
|
|
|
<span class="sd"> Returns: dict with exiftool tags / values</span>
|
|
|
|
<span class="sd"> Exports the following:</span>
|
|
<span class="sd"> EXIF:ImageDescription (may include template)</span>
|
|
<span class="sd"> XMP:Description (may include template)</span>
|
|
<span class="sd"> XMP:Title</span>
|
|
<span class="sd"> IPTC:ObjectName</span>
|
|
<span class="sd"> XMP:TagsList (may include album name, person name, or template)</span>
|
|
<span class="sd"> IPTC:Keywords (may include album name, person name, or template)</span>
|
|
<span class="sd"> IPTC:Caption-Abstract</span>
|
|
<span class="sd"> XMP:Subject (set to keywords + persons)</span>
|
|
<span class="sd"> XMP:PersonInImage</span>
|
|
<span class="sd"> EXIF:GPSLatitudeRef, EXIF:GPSLongitudeRef</span>
|
|
<span class="sd"> EXIF:GPSLatitude, EXIF:GPSLongitude</span>
|
|
<span class="sd"> EXIF:GPSPosition</span>
|
|
<span class="sd"> EXIF:DateTimeOriginal</span>
|
|
<span class="sd"> EXIF:OffsetTimeOriginal</span>
|
|
<span class="sd"> EXIF:ModifyDate</span>
|
|
<span class="sd"> IPTC:DateCreated</span>
|
|
<span class="sd"> IPTC:TimeCreated</span>
|
|
<span class="sd"> QuickTime:CreationDate</span>
|
|
<span class="sd"> QuickTime:CreateDate (UTC)</span>
|
|
<span class="sd"> QuickTime:ModifyDate (UTC)</span>
|
|
<span class="sd"> QuickTime:GPSCoordinates</span>
|
|
<span class="sd"> UserData:GPSCoordinates</span>
|
|
<span class="sd"> XMP:Rating</span>
|
|
|
|
<span class="sd"> Reference:</span>
|
|
<span class="sd"> https://iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201610_1.pdf</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">ExportOptions</span><span class="p">()</span>
|
|
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="p">{</span>
|
|
<span class="s2">"SourceFile"</span><span class="p">:</span> <span class="n">filename</span><span class="p">,</span>
|
|
<span class="s2">"ExifTool:ExifToolVersion"</span><span class="p">:</span> <span class="s2">"12.00"</span><span class="p">,</span>
|
|
<span class="s2">"File:FileName"</span><span class="p">:</span> <span class="n">filename</span><span class="p">,</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="n">filename</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
|
<span class="k">else</span> <span class="p">{}</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">description_template</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="n">render_options</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="p">,</span> <span class="n">expand_inplace</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">inplace_sep</span><span class="o">=</span><span class="s2">", "</span>
|
|
<span class="p">)</span>
|
|
<span class="n">rendered</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span>
|
|
<span class="n">options</span><span class="o">.</span><span class="n">description_template</span><span class="p">,</span> <span class="n">render_options</span>
|
|
<span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="n">description</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">rendered</span><span class="p">)</span> <span class="k">if</span> <span class="n">rendered</span> <span class="k">else</span> <span class="s2">""</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span><span class="p">:</span>
|
|
<span class="n">description</span> <span class="o">=</span> <span class="n">description</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ImageDescription"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Description"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:Caption-Abstract"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
|
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ImageDescription"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Description"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:Caption-Abstract"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">title</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Title"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">title</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:ObjectName"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">title</span>
|
|
|
|
<span class="n">keyword_list</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">merge_exif_keywords</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_keywords</span><span class="p">())</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">replace_keywords</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span><span class="p">)</span>
|
|
|
|
<span class="n">person_list</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">merge_exif_persons</span><span class="p">:</span>
|
|
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
|
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
|
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span>
|
|
<span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">]</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_albums_as_keywords</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">keyword_template</span><span class="p">:</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">render_options</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="p">,</span> <span class="n">none_str</span><span class="o">=</span><span class="n">_OSXPHOTOS_NONE_SENTINEL</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="s2">"/"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">for</span> <span class="n">template_str</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">keyword_template</span><span class="p">:</span>
|
|
<span class="n">rendered</span><span class="p">,</span> <span class="n">unmatched</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span>
|
|
<span class="n">template_str</span><span class="p">,</span> <span class="n">render_options</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">unmatched</span><span class="p">:</span>
|
|
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Unmatched template substitution for template: </span><span class="si">{</span><span class="n">template_str</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">unmatched</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">rendered_keywords</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span><span class="p">:</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[</span><span class="n">keyword</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">rendered_keywords</span><span class="p">]</span>
|
|
|
|
<span class="c1"># filter out any template values that didn't match by looking for sentinel</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[</span>
|
|
<span class="n">keyword</span>
|
|
<span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">rendered_keywords</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">_OSXPHOTOS_NONE_SENTINEL</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">keyword</span>
|
|
<span class="p">]</span>
|
|
|
|
<span class="c1"># check to see if any keywords too long</span>
|
|
<span class="n">long_keywords</span> <span class="o">=</span> <span class="p">[</span>
|
|
<span class="n">long_str</span>
|
|
<span class="k">for</span> <span class="n">long_str</span> <span class="ow">in</span> <span class="n">rendered_keywords</span>
|
|
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">long_str</span><span class="p">)</span> <span class="o">></span> <span class="n">_MAX_IPTC_KEYWORD_LEN</span>
|
|
<span class="p">]</span>
|
|
<span class="k">if</span> <span class="n">long_keywords</span><span class="p">:</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Warning: some keywords exceed max IPTC Keyword length of </span><span class="si">{</span><span class="n">_MAX_IPTC_KEYWORD_LEN</span><span class="si">}</span><span class="s2"> (exiftool will truncate these): </span><span class="si">{</span><span class="n">long_keywords</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered_keywords</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">keyword_list</span><span class="p">:</span>
|
|
<span class="c1"># remove duplicates</span>
|
|
<span class="n">keyword_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">keyword</span><span class="p">)</span> <span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">keyword_list</span><span class="p">)))</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:Keywords"</span><span class="p">]</span> <span class="o">=</span> <span class="n">keyword_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Subject"</span><span class="p">]</span> <span class="o">=</span> <span class="n">keyword_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:TagsList"</span><span class="p">]</span> <span class="o">=</span> <span class="n">keyword_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">persons</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
|
<span class="n">person_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">person_list</span><span class="p">)))</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:PersonInImage"</span><span class="p">]</span> <span class="o">=</span> <span class="n">person_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">face_regions</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">face_info</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_mwg_face_regions_exiftool</span><span class="p">())</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">favorite_rating</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Rating"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">favorite</span> <span class="k">else</span> <span class="mi">0</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
|
|
<span class="p">(</span><span class="n">lat</span><span class="p">,</span> <span class="n">lon</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">location</span>
|
|
<span class="k">if</span> <span class="n">lat</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">lon</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon</span>
|
|
<span class="n">lat_ref</span> <span class="o">=</span> <span class="s2">"N"</span> <span class="k">if</span> <span class="n">lat</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"S"</span>
|
|
<span class="n">lon_ref</span> <span class="o">=</span> <span class="s2">"E"</span> <span class="k">if</span> <span class="n">lon</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"W"</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat_ref</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon_ref</span>
|
|
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"Keys:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"UserData:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
|
|
|
<span class="c1"># process date/time and timezone offset</span>
|
|
<span class="c1"># Photos exports the following fields and sets modify date to creation date</span>
|
|
<span class="c1"># [EXIF] Modify Date : 2020:10:30 00:00:00</span>
|
|
<span class="c1"># [EXIF] Date/Time Original : 2020:10:30 00:00:00</span>
|
|
<span class="c1"># [EXIF] Create Date : 2020:10:30 00:00:00</span>
|
|
<span class="c1"># [IPTC] Digital Creation Date : 2020:10:30</span>
|
|
<span class="c1"># [IPTC] Date Created : 2020:10:30</span>
|
|
<span class="c1">#</span>
|
|
<span class="c1"># for videos:</span>
|
|
<span class="c1"># [QuickTime] CreateDate : 2020:12:11 06:10:10</span>
|
|
<span class="c1"># [QuickTime] ModifyDate : 2020:12:11 06:10:10</span>
|
|
<span class="c1"># [Keys] CreationDate : 2020:12:10 22:10:10-08:00</span>
|
|
<span class="c1"># This code deviates from Photos in one regard:</span>
|
|
<span class="c1"># if photo has modification date, use it otherwise use creation date</span>
|
|
|
|
<span class="n">date</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date</span>
|
|
<span class="n">offsettime</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%z"</span><span class="p">)</span>
|
|
<span class="c1"># find timezone offset in format "-04:00"</span>
|
|
<span class="n">offset</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s2">"([+-]?)([\d]</span><span class="si">{2}</span><span class="s2">)([\d]</span><span class="si">{2}</span><span class="s2">)"</span><span class="p">,</span> <span class="n">offsettime</span><span class="p">)</span>
|
|
<span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># findall returns list of tuples</span>
|
|
<span class="n">offsettime</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">offset</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}{</span><span class="n">offset</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">offset</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
|
|
|
|
<span class="c1"># exiftool expects format to "2015:01:18 12:00:00"</span>
|
|
<span class="n">datetimeoriginal</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:DateTimeOriginal"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetimeoriginal</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:CreateDate"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetimeoriginal</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:OffsetTimeOriginal"</span><span class="p">]</span> <span class="o">=</span> <span class="n">offsettime</span>
|
|
|
|
<span class="n">dateoriginal</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2">"</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:DateCreated"</span><span class="p">]</span> <span class="o">=</span> <span class="n">dateoriginal</span>
|
|
|
|
<span class="n">timeoriginal</span> <span class="o">=</span> <span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="sa">f</span><span class="s2">"%H:%M:%S</span><span class="si">{</span><span class="n">offsettime</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:TimeCreated"</span><span class="p">]</span> <span class="o">=</span> <span class="n">timeoriginal</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date_modified</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
|
|
<span class="ow">and</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_date_modified</span>
|
|
<span class="p">):</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ModifyDate"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date_modified</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span>
|
|
<span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ModifyDate"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
|
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
|
|
<span class="c1"># QuickTime spec specifies times in UTC</span>
|
|
<span class="c1"># QuickTime:CreateDate and ModifyDate are in UTC w/ no timezone</span>
|
|
<span class="c1"># QuickTime:CreationDate must include time offset or Photos shows invalid values</span>
|
|
<span class="c1"># reference: https://exiftool.org/TagNames/QuickTime.html#Keys</span>
|
|
<span class="c1"># https://exiftool.org/forum/index.php?topic=11927.msg64369#msg64369</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"QuickTime:CreationDate"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">datetimeoriginal</span><span class="si">}{</span><span class="n">offsettime</span><span class="si">}</span><span class="s2">"</span>
|
|
|
|
<span class="n">date_utc</span> <span class="o">=</span> <span class="n">datetime_tz_to_utc</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
|
|
<span class="n">creationdate</span> <span class="o">=</span> <span class="n">date_utc</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"QuickTime:CreateDate"</span><span class="p">]</span> <span class="o">=</span> <span class="n">creationdate</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date_modified</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_date_modified</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"QuickTime:ModifyDate"</span><span class="p">]</span> <span class="o">=</span> <span class="n">creationdate</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"QuickTime:ModifyDate"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime_tz_to_utc</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">date_modified</span>
|
|
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
|
|
|
<span class="k">return</span> <span class="n">exif</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_get_mwg_face_regions_exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="sd">"""Return a dict with MWG face regions for use by exiftool"""</span>
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">orientation</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">]:</span>
|
|
<span class="n">w</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">height</span>
|
|
<span class="n">h</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">width</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">w</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">width</span>
|
|
<span class="n">h</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">height</span>
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="p">{}</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAppliedToDimensionsW"</span><span class="p">]</span> <span class="o">=</span> <span class="n">w</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAppliedToDimensionsH"</span><span class="p">]</span> <span class="o">=</span> <span class="n">h</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAppliedToDimensionsUnit"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"pixel"</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionName"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionType"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaX"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaY"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaW"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaH"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaUnit"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionPersonDisplayName"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="c1"># exif["XMP:RegionRectangle"] = []</span>
|
|
<span class="k">for</span> <span class="n">face</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">face_info</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">face</span><span class="o">.</span><span class="n">name</span><span class="p">:</span>
|
|
<span class="k">continue</span>
|
|
<span class="n">area</span> <span class="o">=</span> <span class="n">face</span><span class="o">.</span><span class="n">mwg_rs_area</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionName"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">face</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionType"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"Face"</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaX"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">area</span><span class="o">.</span><span class="n">x</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaY"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">area</span><span class="o">.</span><span class="n">y</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaW"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">area</span><span class="o">.</span><span class="n">w</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaH"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">area</span><span class="o">.</span><span class="n">h</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionAreaUnit"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"normalized"</span><span class="p">)</span>
|
|
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:RegionPersonDisplayName"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">face</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
|
<span class="c1"># exif["XMP:RegionRectangle"].append(f"{area.x},{area.y},{area.h},{area.w}")</span>
|
|
<span class="k">return</span> <span class="n">exif</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_get_exif_keywords</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="sd">"""returns list of keywords found in the file's exif metadata"""</span>
|
|
<span class="n">keywords</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exiftool</span>
|
|
<span class="k">if</span> <span class="n">exif</span><span class="p">:</span>
|
|
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
|
|
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"IPTC:Keywords"</span><span class="p">,</span> <span class="s2">"XMP:TagsList"</span><span class="p">,</span> <span class="s2">"XMP:Subject"</span><span class="p">]:</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">kw</span> <span class="o">=</span> <span class="n">exifdict</span><span class="p">[</span><span class="n">field</span><span class="p">]</span>
|
|
<span class="k">if</span> <span class="n">kw</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">list</span><span class="p">:</span>
|
|
<span class="n">kw</span> <span class="o">=</span> <span class="p">[</span><span class="n">kw</span><span class="p">]</span>
|
|
<span class="n">kw</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">kw</span><span class="p">]</span>
|
|
<span class="n">keywords</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
|
<span class="k">pass</span>
|
|
<span class="k">return</span> <span class="n">keywords</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_get_exif_persons</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|
<span class="sd">"""returns list of persons found in the file's exif metadata"""</span>
|
|
<span class="n">persons</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">exiftool</span>
|
|
<span class="k">if</span> <span class="n">exif</span><span class="p">:</span>
|
|
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
|
|
<span class="k">try</span><span class="p">:</span>
|
|
<span class="n">p</span> <span class="o">=</span> <span class="n">exifdict</span><span class="p">[</span><span class="s2">"XMP:PersonInImage"</span><span class="p">]</span>
|
|
<span class="k">if</span> <span class="n">p</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">list</span><span class="p">:</span>
|
|
<span class="n">p</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="p">]</span>
|
|
<span class="n">p</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">p_</span><span class="p">)</span> <span class="k">for</span> <span class="n">p_</span> <span class="ow">in</span> <span class="n">p</span><span class="p">]</span>
|
|
<span class="n">persons</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
|
|
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
|
<span class="k">pass</span>
|
|
<span class="k">return</span> <span class="n">persons</span>
|
|
|
|
<div class="viewcode-block" id="PhotoExporter.exiftool_json_sidecar"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">[docs]</a> <span class="k">def</span> <span class="nf">exiftool_json_sidecar</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">tag_groups</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">,</span>
|
|
<span class="n">filename</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="sd">"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.</span>
|
|
<span class="sd"> Does not include all the EXIF fields as those are likely already in the image.</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> options (ExportOptions): options for export</span>
|
|
<span class="sd"> tag_groups (bool, default=True): if True, include tag groups in the output</span>
|
|
<span class="sd"> filename (str): name of source image file (without path); if not None, exiftool JSON signature will be included; if None, signature will not be included</span>
|
|
|
|
<span class="sd"> Returns: dict with exiftool tags / values</span>
|
|
|
|
<span class="sd"> Exports the following:</span>
|
|
<span class="sd"> EXIF:ImageDescription</span>
|
|
<span class="sd"> XMP:Description (may include template)</span>
|
|
<span class="sd"> IPTC:CaptionAbstract</span>
|
|
<span class="sd"> XMP:Title</span>
|
|
<span class="sd"> IPTC:ObjectName</span>
|
|
<span class="sd"> XMP:TagsList</span>
|
|
<span class="sd"> IPTC:Keywords (may include album name, person name, or template)</span>
|
|
<span class="sd"> XMP:Subject (set to keywords + person)</span>
|
|
<span class="sd"> XMP:PersonInImage</span>
|
|
<span class="sd"> EXIF:GPSLatitudeRef, EXIF:GPSLongitudeRef</span>
|
|
<span class="sd"> EXIF:GPSLatitude, EXIF:GPSLongitude</span>
|
|
<span class="sd"> EXIF:GPSPosition</span>
|
|
<span class="sd"> EXIF:DateTimeOriginal</span>
|
|
<span class="sd"> EXIF:OffsetTimeOriginal</span>
|
|
<span class="sd"> EXIF:ModifyDate</span>
|
|
<span class="sd"> IPTC:DigitalCreationDate</span>
|
|
<span class="sd"> IPTC:DateCreated</span>
|
|
<span class="sd"> QuickTime:CreationDate</span>
|
|
<span class="sd"> QuickTime:CreateDate (UTC)</span>
|
|
<span class="sd"> QuickTime:ModifyDate (UTC)</span>
|
|
<span class="sd"> QuickTime:GPSCoordinates</span>
|
|
<span class="sd"> UserData:GPSCoordinates</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">ExportOptions</span><span class="p">()</span>
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_dict</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">filename</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">tag_groups</span><span class="p">:</span>
|
|
<span class="c1"># strip tag groups</span>
|
|
<span class="n">exif_new</span> <span class="o">=</span> <span class="p">{}</span>
|
|
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">exif</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
|
<span class="n">k</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s2">".*:"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
|
|
<span class="n">exif_new</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
|
<span class="n">exif</span> <span class="o">=</span> <span class="n">exif_new</span>
|
|
|
|
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">exif</span><span class="p">])</span></div>
|
|
|
|
<span class="k">def</span> <span class="nf">_xmp_sidecar</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="p">,</span>
|
|
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="n">extension</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
|
<span class="p">):</span>
|
|
<span class="sd">"""returns string for XMP sidecar</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> options (ExportOptions): options for export</span>
|
|
<span class="sd"> extension (t.Optional[str]): which extension to use for SidecarForExtension property</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">ExportOptions</span><span class="p">()</span>
|
|
|
|
<span class="n">xmp_template_file</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="n">_XMP_TEMPLATE_NAME</span> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_beta</span> <span class="k">else</span> <span class="n">_XMP_TEMPLATE_NAME_BETA</span>
|
|
<span class="p">)</span>
|
|
<span class="n">xmp_template</span> <span class="o">=</span> <span class="n">Template</span><span class="p">(</span><span class="n">filename</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">_TEMPLATE_DIR</span><span class="p">,</span> <span class="n">xmp_template_file</span><span class="p">))</span>
|
|
|
|
<span class="k">if</span> <span class="n">extension</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="n">extension</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span>
|
|
<span class="n">extension</span> <span class="o">=</span> <span class="n">extension</span><span class="o">.</span><span class="n">suffix</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">if</span> <span class="n">extension</span><span class="o">.</span><span class="n">suffix</span> <span class="k">else</span> <span class="kc">None</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">description_template</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="n">render_options</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="p">,</span> <span class="n">expand_inplace</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">inplace_sep</span><span class="o">=</span><span class="s2">", "</span>
|
|
<span class="p">)</span>
|
|
<span class="n">rendered</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span>
|
|
<span class="n">options</span><span class="o">.</span><span class="n">description_template</span><span class="p">,</span> <span class="n">render_options</span>
|
|
<span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
|
<span class="n">description</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">rendered</span><span class="p">)</span> <span class="k">if</span> <span class="n">rendered</span> <span class="k">else</span> <span class="s2">""</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span><span class="p">:</span>
|
|
<span class="n">description</span> <span class="o">=</span> <span class="n">description</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">description</span> <span class="o">=</span> <span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">description</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="s2">""</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="n">keyword_list</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">merge_exif_keywords</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_keywords</span><span class="p">())</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">replace_keywords</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">keywords</span><span class="p">)</span>
|
|
|
|
<span class="c1"># TODO: keyword handling in this and _exiftool_json_sidecar is</span>
|
|
<span class="c1"># good candidate for pulling out in a function</span>
|
|
|
|
<span class="n">person_list</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">merge_exif_persons</span><span class="p">:</span>
|
|
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
|
|
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
|
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
|
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span>
|
|
<span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">]</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">use_albums_as_keywords</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">keyword_template</span><span class="p">:</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="n">render_options</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="p">,</span> <span class="n">none_str</span><span class="o">=</span><span class="n">_OSXPHOTOS_NONE_SENTINEL</span><span class="p">,</span> <span class="n">path_sep</span><span class="o">=</span><span class="s2">"/"</span>
|
|
<span class="p">)</span>
|
|
<span class="k">for</span> <span class="n">template_str</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">keyword_template</span><span class="p">:</span>
|
|
<span class="n">rendered</span><span class="p">,</span> <span class="n">unmatched</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span>
|
|
<span class="n">template_str</span><span class="p">,</span> <span class="n">render_options</span>
|
|
<span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">unmatched</span><span class="p">:</span>
|
|
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"Unmatched template substitution for template: </span><span class="si">{</span><span class="n">template_str</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">unmatched</span><span class="si">}</span><span class="s2">"</span>
|
|
<span class="p">)</span>
|
|
<span class="n">rendered_keywords</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">strip</span><span class="p">:</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[</span><span class="n">keyword</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">rendered_keywords</span><span class="p">]</span>
|
|
|
|
<span class="c1"># filter out any template values that didn't match by looking for sentinel</span>
|
|
<span class="n">rendered_keywords</span> <span class="o">=</span> <span class="p">[</span>
|
|
<span class="n">keyword</span>
|
|
<span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">rendered_keywords</span>
|
|
<span class="k">if</span> <span class="n">_OSXPHOTOS_NONE_SENTINEL</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">keyword</span>
|
|
<span class="p">]</span>
|
|
|
|
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered_keywords</span><span class="p">)</span>
|
|
|
|
<span class="c1"># remove duplicates</span>
|
|
<span class="c1"># sorted mainly to make testing the XMP file easier</span>
|
|
<span class="k">if</span> <span class="n">keyword_list</span><span class="p">:</span>
|
|
<span class="n">keyword_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">keyword_list</span><span class="p">)))</span>
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">persons</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
|
<span class="n">person_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">person_list</span><span class="p">)))</span>
|
|
|
|
<span class="n">subject_list</span> <span class="o">=</span> <span class="n">keyword_list</span>
|
|
|
|
<span class="n">latlon</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">location</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">location</span> <span class="k">else</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">favorite_rating</span><span class="p">:</span>
|
|
<span class="n">rating</span> <span class="o">=</span> <span class="mi">5</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">favorite</span> <span class="k">else</span> <span class="mi">0</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">rating</span> <span class="o">=</span> <span class="kc">None</span>
|
|
|
|
<span class="n">xmp_str</span> <span class="o">=</span> <span class="n">xmp_template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span>
|
|
<span class="n">photo</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span>
|
|
<span class="n">description</span><span class="o">=</span><span class="n">description</span><span class="p">,</span>
|
|
<span class="n">keywords</span><span class="o">=</span><span class="n">keyword_list</span><span class="p">,</span>
|
|
<span class="n">persons</span><span class="o">=</span><span class="n">person_list</span><span class="p">,</span>
|
|
<span class="n">subjects</span><span class="o">=</span><span class="n">subject_list</span><span class="p">,</span>
|
|
<span class="n">extension</span><span class="o">=</span><span class="n">extension</span><span class="p">,</span>
|
|
<span class="n">location</span><span class="o">=</span><span class="n">latlon</span><span class="p">,</span>
|
|
<span class="n">version</span><span class="o">=</span><span class="n">__version__</span><span class="p">,</span>
|
|
<span class="n">rating</span><span class="o">=</span><span class="n">rating</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># remove extra lines that mako inserts from template</span>
|
|
<span class="n">xmp_str</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">xmp_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span> <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">""</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">xmp_str</span>
|
|
|
|
<span class="k">def</span> <span class="nf">_write_sidecar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">sidecar_str</span><span class="p">):</span>
|
|
<span class="sd">"""write sidecar_str to filename</span>
|
|
<span class="sd"> used for exporting sidecar info"""</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">filename</span> <span class="ow">or</span> <span class="n">sidecar_str</span><span class="p">):</span>
|
|
<span class="k">raise</span> <span class="p">(</span>
|
|
<span class="ne">ValueError</span><span class="p">(</span>
|
|
<span class="sa">f</span><span class="s2">"filename </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2"> and sidecar_str </span><span class="si">{</span><span class="n">sidecar_str</span><span class="si">}</span><span class="s2"> must not be None"</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="c1"># TODO: catch exception?</span>
|
|
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span>
|
|
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">sidecar_str</span><span class="p">)</span>
|
|
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span></div>
|
|
|
|
|
|
<span class="k">def</span> <span class="nf">_check_export_suffix</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">edited</span><span class="p">):</span>
|
|
<span class="sd">"""Helper function for exporting photos to check file extensions of destination path.</span>
|
|
|
|
<span class="sd"> Checks that dst file extension is appropriate for the src.</span>
|
|
<span class="sd"> If edited=True, will use src file extension of ".jpeg" if None provided for src.</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> src: path to source file or None.</span>
|
|
<span class="sd"> dest: path to destination file.</span>
|
|
<span class="sd"> edited: set to True if exporting an edited photo.</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> True if src and dest extensions are OK, else False.</span>
|
|
|
|
<span class="sd"> Raises:</span>
|
|
<span class="sd"> ValueError if edited is False and src is None</span>
|
|
<span class="sd"> """</span>
|
|
|
|
<span class="c1"># check extension of destination</span>
|
|
<span class="k">if</span> <span class="n">src</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|
<span class="c1"># use suffix from edited file</span>
|
|
<span class="n">actual_suffix</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">src</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span>
|
|
<span class="k">elif</span> <span class="n">edited</span><span class="p">:</span>
|
|
<span class="c1"># use .jpeg as that's probably correct</span>
|
|
<span class="n">actual_suffix</span> <span class="o">=</span> <span class="s2">".jpeg"</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"src must not be None if edited=False"</span><span class="p">)</span>
|
|
|
|
<span class="c1"># Photo's often converts .JPG to .jpeg or .tif to .tiff on import</span>
|
|
<span class="n">dest_ext</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
|
<span class="n">actual_ext</span> <span class="o">=</span> <span class="n">actual_suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
|
<span class="n">suffixes</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">([</span><span class="n">dest_ext</span><span class="p">,</span> <span class="n">actual_ext</span><span class="p">])</span>
|
|
<span class="k">return</span> <span class="p">(</span>
|
|
<span class="n">dest_ext</span> <span class="o">==</span> <span class="n">actual_ext</span>
|
|
<span class="ow">or</span> <span class="n">suffixes</span> <span class="o">==</span> <span class="p">[</span><span class="s2">".jpeg"</span><span class="p">,</span> <span class="s2">".jpg"</span><span class="p">]</span>
|
|
<span class="ow">or</span> <span class="n">suffixes</span> <span class="o">==</span> <span class="p">[</span><span class="s2">".tif"</span><span class="p">,</span> <span class="s2">".tiff"</span><span class="p">]</span>
|
|
<span class="p">)</span>
|
|
|
|
|
|
<span class="k">def</span> <span class="nf">rename_jpeg_files</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">jpeg_ext</span><span class="p">,</span> <span class="n">fileutil</span><span class="p">):</span>
|
|
<span class="sd">"""rename any jpeg files in files so that extension matches jpeg_ext</span>
|
|
|
|
<span class="sd"> Args:</span>
|
|
<span class="sd"> files: list of file paths</span>
|
|
<span class="sd"> jpeg_ext: extension to use for jpeg files found in files, e.g. "jpg"</span>
|
|
<span class="sd"> fileutil: a FileUtil object</span>
|
|
|
|
<span class="sd"> Returns:</span>
|
|
<span class="sd"> list of files with updated names</span>
|
|
|
|
<span class="sd"> Note: If non-jpeg files found, they will be ignore and returned in the return list</span>
|
|
<span class="sd"> """</span>
|
|
<span class="n">jpeg_ext</span> <span class="o">=</span> <span class="s2">"."</span> <span class="o">+</span> <span class="n">jpeg_ext</span>
|
|
<span class="n">jpegs</span> <span class="o">=</span> <span class="p">[</span><span class="s2">".jpeg"</span><span class="p">,</span> <span class="s2">".jpg"</span><span class="p">]</span>
|
|
<span class="n">new_files</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">for</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
|
|
<span class="n">path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="n">path</span><span class="o">.</span><span class="n">suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">jpegs</span> <span class="ow">and</span> <span class="n">path</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="n">jpeg_ext</span><span class="p">:</span>
|
|
<span class="n">new_file</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">stem</span> <span class="o">+</span> <span class="n">jpeg_ext</span><span class="p">)</span>
|
|
<span class="n">fileutil</span><span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">new_file</span><span class="p">)</span>
|
|
<span class="n">new_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_file</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">new_files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">new_files</span>
|
|
</pre></div>
|
|
</article>
|
|
</div>
|
|
<footer>
|
|
|
|
<div class="related-pages">
|
|
|
|
|
|
</div>
|
|
<div class="bottom-of-page">
|
|
<div class="left-details">
|
|
<div class="copyright">
|
|
Copyright © 2021, Rhet Turnbull
|
|
</div>
|
|
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
|
|
|
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
|
|
|
</div>
|
|
<div class="right-details">
|
|
<div class="icons">
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</footer>
|
|
</div>
|
|
<aside class="toc-drawer no-toc">
|
|
|
|
|
|
|
|
</aside>
|
|
</div>
|
|
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
|
<script src="../../_static/jquery.js"></script>
|
|
<script src="../../_static/underscore.js"></script>
|
|
<script src="../../_static/doctools.js"></script>
|
|
<script src="../../_static/scripts/furo.js"></script>
|
|
<script src="../../_static/clipboard.min.js"></script>
|
|
<script src="../../_static/copybutton.js"></script>
|
|
</body>
|
|
</html> |