Files
osxphotos/docs/_modules/osxphotos/photoinfo.html
2023-04-08 12:04:54 -07:00

2343 lines
296 KiB
HTML

<!doctype html>
<html class="no-js" lang="en">
<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-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photoinfo - osxphotos 0.59.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<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.59.2 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.59.2 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 Reference</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.photoinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; PhotoInfo class: Represents a single photo in the Photos library and provides access to the photo&#39;s attributes</span>
<span class="sd">PhotosDB.photos() returns a list of PhotoInfo objects</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">import</span> <span class="nn">contextlib</span>
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</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">os.path</span>
<span class="kn">import</span> <span class="nn">pathlib</span>
<span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span><span class="p">,</span> <span class="n">timezone</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">cached_property</span>
<span class="kn">from</span> <span class="nn">types</span> <span class="kn">import</span> <span class="n">SimpleNamespace</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">yaml</span>
<span class="kn">from</span> <span class="nn">osxmetadata</span> <span class="kn">import</span> <span class="n">OSXMetaData</span>
<span class="kn">import</span> <span class="nn">osxphotos</span>
<span class="kn">from</span> <span class="nn">._constants</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">_DB_TABLE_NAMES</span><span class="p">,</span>
<span class="n">_MOVIE_TYPE</span><span class="p">,</span>
<span class="n">_PHOTO_TYPE</span><span class="p">,</span>
<span class="n">_PHOTOS_4_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_4_ALBUM_TYPE_ALBUM</span><span class="p">,</span>
<span class="n">_PHOTOS_4_ALBUM_TYPE_PROJECT</span><span class="p">,</span>
<span class="n">_PHOTOS_4_ALBUM_TYPE_SLIDESHOW</span><span class="p">,</span>
<span class="n">_PHOTOS_4_ROOT_FOLDER</span><span class="p">,</span>
<span class="n">_PHOTOS_4_VERSION</span><span class="p">,</span>
<span class="n">_PHOTOS_5_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_IMPORT_SESSION_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_PROJECT_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_SHARED_ALBUM_KIND</span><span class="p">,</span>
<span class="n">_PHOTOS_5_SHARED_DERIVATIVE_PATH</span><span class="p">,</span>
<span class="n">_PHOTOS_5_SHARED_PHOTO_PATH</span><span class="p">,</span>
<span class="n">_PHOTOS_5_VERSION</span><span class="p">,</span>
<span class="n">_PHOTOS_8_SHARED_DERIVATIVE_PATH</span><span class="p">,</span>
<span class="n">_PHOTOS_8_SHARED_PHOTO_PATH</span><span class="p">,</span>
<span class="n">BURST_DEFAULT_PICK</span><span class="p">,</span>
<span class="n">BURST_KEY</span><span class="p">,</span>
<span class="n">BURST_NOT_SELECTED</span><span class="p">,</span>
<span class="n">BURST_SELECTED</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="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">.adjustmentsinfo</span> <span class="kn">import</span> <span class="n">AdjustmentsInfo</span>
<span class="kn">from</span> <span class="nn">.albuminfo</span> <span class="kn">import</span> <span class="n">AlbumInfo</span><span class="p">,</span> <span class="n">ImportInfo</span><span class="p">,</span> <span class="n">ProjectInfo</span>
<span class="kn">from</span> <span class="nn">.exifinfo</span> <span class="kn">import</span> <span class="n">ExifInfo</span>
<span class="kn">from</span> <span class="nn">.exiftool</span> <span class="kn">import</span> <span class="n">ExifToolCaching</span><span class="p">,</span> <span class="n">get_exiftool_path</span>
<span class="kn">from</span> <span class="nn">.momentinfo</span> <span class="kn">import</span> <span class="n">MomentInfo</span>
<span class="kn">from</span> <span class="nn">.personinfo</span> <span class="kn">import</span> <span class="n">FaceInfo</span><span class="p">,</span> <span class="n">PersonInfo</span>
<span class="kn">from</span> <span class="nn">.photoexporter</span> <span class="kn">import</span> <span class="n">ExportOptions</span><span class="p">,</span> <span class="n">PhotoExporter</span>
<span class="kn">from</span> <span class="nn">.phototemplate</span> <span class="kn">import</span> <span class="n">PhotoTemplate</span><span class="p">,</span> <span class="n">RenderOptions</span>
<span class="kn">from</span> <span class="nn">.placeinfo</span> <span class="kn">import</span> <span class="n">PlaceInfo4</span><span class="p">,</span> <span class="n">PlaceInfo5</span>
<span class="kn">from</span> <span class="nn">.query_builder</span> <span class="kn">import</span> <span class="n">get_query</span>
<span class="kn">from</span> <span class="nn">.scoreinfo</span> <span class="kn">import</span> <span class="n">ScoreInfo</span>
<span class="kn">from</span> <span class="nn">.searchinfo</span> <span class="kn">import</span> <span class="n">SearchInfo</span>
<span class="kn">from</span> <span class="nn">.text_detection</span> <span class="kn">import</span> <span class="n">detect_text</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="p">,</span> <span class="n">get_uti_for_extension</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">hexdigest</span><span class="p">,</span> <span class="n">list_directory</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotoInfo&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotoInfoNone&quot;</span><span class="p">,</span> <span class="s2">&quot;frozen_photoinfo_factory&quot;</span><span class="p">]</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;osxphotos&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="PhotoInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo">[docs]</a><span class="k">class</span> <span class="nc">PhotoInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Info about a specific photo, contains all the details about the photo</span>
<span class="sd"> including keywords, persons, albums, uuid, path, etc.</span>
<span class="sd"> &quot;&quot;&quot;</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">db</span><span class="p">:</span> <span class="s2">&quot;osxphotos.PhotosDB&quot;</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">info</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">uuid</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">=</span> <span class="n">info</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">:</span> <span class="s2">&quot;osxphotos.PhotosDB&quot;</span> <span class="o">=</span> <span class="n">db</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_exiftool_path</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">_db</span><span class="o">.</span><span class="n">_verbose</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">filename</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;filename of the picture&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span>
<span class="p">):</span>
<span class="c1"># return the JPEG version as that&#39;s what Photos 5+ does</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;filename&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;filename&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">original_filename</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;original filename of the picture</span>
<span class="sd"> Photos 5 mangles filenames upon import&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span>
<span class="p">):</span>
<span class="c1"># return the JPEG version as that&#39;s what Photos 5+ does</span>
<span class="n">original_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;originalFilename&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">original_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;originalFilename&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="n">original_name</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;image creation date as timezone aware datetime object&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">date_modified</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;image modification date as timezone aware datetime object</span>
<span class="sd"> or None if no modification date set&quot;&quot;&quot;</span>
<span class="c1"># Photos &lt;= 4 provides no way to get date of adjustment and will update</span>
<span class="c1"># lastmodifieddate anytime photo database record is updated (e.g. adding tags)</span>
<span class="c1"># only report lastmodified date for Photos &lt;=4 if photo is edited;</span>
<span class="c1"># even in this case, the date could be incorrect</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="n">imagedate</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">imagedate</span><span class="p">:</span>
<span class="n">seconds</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span> <span class="ow">or</span> <span class="mi">0</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="k">return</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">tzoffset</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;timezone offset from UTC in seconds&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">path</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;absolute path on disk of the original picture&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_path</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;isMissing&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">photopath</span> <span class="c1"># path would be meaningless until downloaded</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_4</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_5</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photopath</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_path</span> <span class="o">=</span> <span class="n">photopath</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_5</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns candidate path for original photo on Photos &gt;= version 5&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;shared&quot;</span><span class="p">]:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_5_shared</span><span class="p">()</span>
<span class="k">return</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">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">],</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;filename&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">)</span>
<span class="k">else</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_masters_path</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">],</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;filename&quot;</span><span class="p">],</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_path_5_shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns candidate path for shared photo on Photos &gt;= version 5&quot;&quot;&quot;</span>
<span class="c1"># shared library path differs on Photos 5-7, Photos 8+</span>
<span class="n">shared_path</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">_PHOTOS_8_SHARED_PHOTO_PATH</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&gt;=</span> <span class="mi">8</span>
<span class="k">else</span> <span class="n">_PHOTOS_5_SHARED_PHOTO_PATH</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
<span class="k">return</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="n">shared_path</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">],</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;filename&quot;</span><span class="p">],</span>
<span class="p">)</span>
<span class="c1"># a shared video has two files, the poster image and the video</span>
<span class="c1"># the poster (image frame shown in Photos) is named UUID.poster.JPG</span>
<span class="c1"># the video file is named UUID.medium.MP4</span>
<span class="c1"># this method returns the path to the video file</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">.medium.MP4&quot;</span>
<span class="k">return</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="n">shared_path</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">],</span>
<span class="n">filename</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_path_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns candidate path for original photo on Photos &lt;= version 4&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;has_raw&quot;</span><span class="p">]:</span>
<span class="c1"># return the path to JPEG even if RAW is original</span>
<span class="n">vol</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbvolumes</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;volumeId&quot;</span><span class="p">]]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;volumeId&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">vol</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">photopath</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="s2">&quot;/Volumes&quot;</span><span class="p">,</span> <span class="n">vol</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;imagePath&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_masters_path</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;imagePath&quot;</span><span class="p">],</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">vol</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;volume&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">vol</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">photopath</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="s2">&quot;/Volumes&quot;</span><span class="p">,</span> <span class="n">vol</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imagePath&quot;</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_masters_path</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imagePath&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">path_edited</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;absolute path on disk of the edited picture&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot; None if photo has not been edited &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_4</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_5</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photopath</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;edited file for UUID </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2"> should be at </span><span class="si">{</span><span class="n">photopath</span><span class="si">}</span><span class="s2"> but does not appear to exist&quot;</span>
<span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_path_edited</span> <span class="o">=</span> <span class="n">photopath</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited</span>
<span class="k">def</span> <span class="nf">_path_edited_5</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns candidate path_edited for Photos &gt;= 5 or None if cannot be determined&quot;&quot;&quot;</span>
<span class="c1"># In Photos 5.0 / Catalina / MacOS 10.15:</span>
<span class="c1"># edited photos appear to always be converted to .jpeg and stored in</span>
<span class="c1"># library_name/resources/renders/X/UUID_1_201_a.jpeg</span>
<span class="c1"># where X = first letter of UUID</span>
<span class="c1"># and UUID = UUID of image</span>
<span class="c1"># this seems to be true even for photos not copied to Photos library and</span>
<span class="c1"># where original format was not jpg/jpeg</span>
<span class="c1"># if more than one edit, previous edit is stored as UUID_p.jpeg</span>
<span class="c1">#</span>
<span class="c1"># In Photos 6.0 / Big Sur, the edited image is a .heic if the photo isn&#39;t a jpeg,</span>
<span class="c1"># otherwise it&#39;s a jpeg. It could also be a jpeg if photo library upgraded from earlier</span>
<span class="c1"># version.</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Wrong database format!&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hasAdjustments&quot;</span><span class="p">]:</span>
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="n">filename</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="n">_PHOTO_TYPE</span><span class="p">:</span>
<span class="c1"># it&#39;s a photo</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">!=</span> <span class="mi">5</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span> <span class="o">==</span> <span class="s2">&quot;public.heic&quot;</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">_1_201_a.heic&quot;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">_1_201_a.jpeg&quot;</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="n">_MOVIE_TYPE</span><span class="p">:</span>
<span class="c1"># it&#39;s a movie</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">_2_0_a.mov&quot;</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># don&#39;t know what it is!</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;WARNING: unknown type </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s1">&#39;type&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">return</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">library</span><span class="p">,</span> <span class="s2">&quot;resources&quot;</span><span class="p">,</span> <span class="s2">&quot;renders&quot;</span><span class="p">,</span> <span class="n">directory</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">_get_predicted_path_edited_4</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;return predicted path_edited for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="n">edit_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;edit_resource_id_photo&quot;</span><span class="p">]</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">edit_id</span><span class="p">)</span>
<span class="c1"># figure out what kind it is and build filename</span>
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
<span class="k">if</span> <span class="n">uti_edited</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_edited</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_edited</span><span class="p">)</span>
<span class="k">if</span> <span class="n">ext</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;fullsizeoutput_</span><span class="si">{</span><span class="n">file_id</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">&quot;</span>
<span class="k">return</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">library</span><span class="p">,</span> <span class="s2">&quot;resources&quot;</span><span class="p">,</span> <span class="s2">&quot;media&quot;</span><span class="p">,</span> <span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">folder_id</span><span class="p">,</span> <span class="n">nn_id</span><span class="p">,</span> <span class="n">filename</span>
<span class="p">)</span>
<span class="c1"># if we get here, we couldn&#39;t figure out the extension</span>
<span class="c1"># so try to figure out the type and build the filename</span>
<span class="n">type_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">type_</span> <span class="o">==</span> <span class="n">_PHOTO_TYPE</span><span class="p">:</span>
<span class="c1"># it&#39;s a photo</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;fullsizeoutput_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.jpeg&quot;</span>
<span class="k">elif</span> <span class="n">type_</span> <span class="o">==</span> <span class="n">_MOVIE_TYPE</span><span class="p">:</span>
<span class="c1"># it&#39;s a movie</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;fullsizeoutput_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov&quot;</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="sa">f</span><span class="s2">&quot;Unknown type </span><span class="si">{</span><span class="n">type_</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</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">library</span><span class="p">,</span> <span class="s2">&quot;resources&quot;</span><span class="p">,</span> <span class="s2">&quot;media&quot;</span><span class="p">,</span> <span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">folder_id</span><span class="p">,</span> <span class="n">nn_id</span><span class="p">,</span> <span class="n">filename</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_path_edited_4</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;return path_edited for Photos &lt;= 4; modified version of code in PhotoInfo to debug #859&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hasAdjustments&quot;</span><span class="p">]:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">edit_id</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;edit_resource_id&quot;</span><span class="p">]:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_predicted_path_edited_4</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;ERROR: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">photopath</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># the heuristic failed, so try to find the file</span>
<span class="n">rootdir</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">photopath</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">parent</span>
<span class="n">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">photopath</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
<span class="k">for</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">filelist</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">rootdir</span><span class="p">):</span>
<span class="k">if</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">filelist</span><span class="p">:</span>
<span class="n">photopath</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">dirname</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span>
<span class="k">break</span>
<span class="c1"># check again to see if we found a valid file</span>
<span class="k">if</span> <span class="n">photopath</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;MISSING PATH: edited file for UUID </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2"> should be at </span><span class="si">{</span><span class="n">photopath</span><span class="si">}</span><span class="s2"> but does not appear to exist&quot;</span>
<span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> hasAdjustments but edit_resource_id is None&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">path_edited_live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return path to edited version of live photo movie&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_live_photo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_live_photo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_4_live_photo</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_live_photo</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_5_live_photo</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_edited_live_photo</span>
<span class="k">def</span> <span class="nf">_get_predicted_path_edited_live_photo_4</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;return predicted path_edited for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="c1"># need the resource id for the video, not the photo (edit_resource_id is for photo)</span>
<span class="k">if</span> <span class="n">edit_id</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;edit_resource_id_video&quot;</span><span class="p">]:</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">edit_id</span><span class="p">)</span>
<span class="c1"># figure out what kind it is and build filename</span>
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;videocomplementoutput_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov&quot;</span>
<span class="k">return</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">library</span><span class="p">,</span> <span class="s2">&quot;resources&quot;</span><span class="p">,</span> <span class="s2">&quot;media&quot;</span><span class="p">,</span> <span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">folder_id</span><span class="p">,</span> <span class="n">nn_id</span><span class="p">,</span> <span class="n">filename</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">_path_edited_4_live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return path_edited_live_photo for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Wrong database format!&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_predicted_path_edited_live_photo_4</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photopath</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># the heuristic failed, so try to find the file</span>
<span class="n">rootdir</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">photopath</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">parent</span>
<span class="n">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">photopath</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</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">join</span><span class="p">(</span><span class="n">dirname</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span>
<span class="k">for</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">filelist</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">rootdir</span><span class="p">)</span>
<span class="k">if</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">filelist</span>
<span class="p">),</span>
<span class="kc">None</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_edited_5_live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return path_edited_live_photo for Photos &gt;= 5&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Wrong database format!&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</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">_info</span><span class="p">[</span><span class="s2">&quot;hasAdjustments&quot;</span><span class="p">]:</span>
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">_2_100_a.mov&quot;</span>
<span class="n">photopath</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">library</span><span class="p">,</span> <span class="s2">&quot;resources&quot;</span><span class="p">,</span> <span class="s2">&quot;renders&quot;</span><span class="p">,</span> <span class="n">directory</span><span class="p">,</span> <span class="n">filename</span>
<span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">path_raw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;absolute path of associated RAW image or None if there is not one&quot;&quot;&quot;</span>
<span class="c1"># In Photos 5, raw is in same folder as original but with _4.ext</span>
<span class="c1"># Unless &quot;Copy Items to the Photos Library&quot; is not checked</span>
<span class="c1"># then RAW image is not renamed but has same name is jpeg buth with raw extension</span>
<span class="c1"># Current implementation finds images with the correct raw UTI extension</span>
<span class="c1"># in same folder as the original and with same stem as original in form: original_stem*.raw_ext</span>
<span class="c1"># TODO: I don&#39;t like this -- would prefer a more deterministic approach but until I have more</span>
<span class="c1"># data on how Photos stores and retrieves RAW images, this seems to be working</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;isMissing&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span> <span class="c1"># path would be meaningless until downloaded</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span> <span class="c1"># no raw image to get path for</span>
<span class="c1"># if self._info[&quot;shared&quot;]:</span>
<span class="c1"># # shared photo</span>
<span class="c1"># photopath = os.path.join(</span>
<span class="c1"># self._db._library_path,</span>
<span class="c1"># _PHOTOS_5_SHARED_PHOTO_PATH,</span>
<span class="c1"># self._info[&quot;directory&quot;],</span>
<span class="c1"># self._info[&quot;filename&quot;],</span>
<span class="c1"># )</span>
<span class="c1"># return photopath</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_raw_4</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">isreference</span><span class="p">:</span>
<span class="n">filestem</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">_info</span><span class="p">[</span><span class="s2">&quot;filename&quot;</span><span class="p">])</span><span class="o">.</span><span class="n">stem</span>
<span class="c1"># raw_ext = get_preferred_uti_extension(self._info[&quot;UTI_raw&quot;])</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">):</span>
<span class="n">filepath</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">filepath</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_masters_path</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;directory&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">raw_file</span> <span class="o">:=</span> <span class="n">list_directory</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">startswith</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filestem</span><span class="si">}</span><span class="s2">_4&quot;</span><span class="p">):</span>
<span class="n">photopath</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="o">/</span> <span class="n">raw_file</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">photopath</span><span class="p">)</span> <span class="k">if</span> <span class="n">photopath</span><span class="o">.</span><span class="n">is_file</span><span class="p">()</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># is a reference</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</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="s2">&quot;/Volumes&quot;</span><span class="p">)</span>
<span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_volume&quot;</span><span class="p">]</span>
<span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_relative_path&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">photopath</span><span class="p">)</span> <span class="k">if</span> <span class="n">photopath</span><span class="o">.</span><span class="n">is_file</span><span class="p">()</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># don&#39;t have the path details</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_raw_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path_raw for Photos &lt;= version 4&quot;&quot;&quot;</span>
<span class="n">vol</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_info&quot;</span><span class="p">][</span><span class="s2">&quot;volume&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">vol</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">photopath</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="s2">&quot;/Volumes&quot;</span><span class="p">,</span> <span class="n">vol</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_info&quot;</span><span class="p">][</span><span class="s2">&quot;imagePath&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_masters_path</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_info&quot;</span><span class="p">][</span><span class="s2">&quot;imagePath&quot;</span><span class="p">]</span>
<span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;MISSING PATH: RAW photo for UUID </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2"> should be at </span><span class="si">{</span><span class="n">photopath</span><span class="si">}</span><span class="s2"> but does not appear to exist&quot;</span>
<span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">description</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;long / extended description of picture&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;extendedDescription&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">persons</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of persons in picture&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbpersons_pk</span><span class="p">[</span><span class="n">pk</span><span class="p">][</span><span class="s2">&quot;fullname&quot;</span><span class="p">]</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;persons&quot;</span><span class="p">]]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">person_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of PersonInfo objects for person in picture&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">PersonInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;persons&quot;</span><span class="p">]</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_personinfo</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">face_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of FaceInfo objects for faces in picture&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">faces</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_faceinfo_uuid</span><span class="p">[</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">_faceinfo</span> <span class="o">=</span> <span class="p">[</span><span class="n">FaceInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="n">pk</span><span class="p">)</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="n">faces</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># no faces</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_faceinfo</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">moment_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Moment photo belongs to&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_moment</span> <span class="o">=</span> <span class="n">MomentInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">moment_pk</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;momentID&quot;</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_moment</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">albums</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of albums picture is contained in&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_albums</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">album_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span>
<span class="p">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbalbum_details</span><span class="p">[</span><span class="n">album</span><span class="p">][</span><span class="s2">&quot;title&quot;</span><span class="p">]</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">album_uuids</span><span class="p">}</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_albums</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_albums</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;If photo is burst photo, list of albums it is contained in as well as any albums the key photo is contained in, otherwise returns self.albums&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_albums</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_albums</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of AlbumInfo objects representing albums the photo is contained in&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">album_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">AlbumInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">album_uuids</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;If photo is a burst photo, returns list of AlbumInfo objects representing albums the photo is contained in as well as albums the burst key photo is contained in, otherwise returns self.album_info.&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">burst_album_info</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
<span class="k">if</span> <span class="n">photo</span><span class="o">.</span><span class="n">burst_key</span><span class="p">:</span>
<span class="n">burst_album_info</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_album_info</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">import_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;ImportInfo object representing import session for the photo or None if no import session&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">ImportInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;import_uuid&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_import_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">project_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of AlbumInfo objects representing projects for the photo or None if no projects&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">project_uuids</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_album_uuids</span><span class="p">(</span><span class="n">project</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">ProjectInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">album</span><span class="p">)</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="n">project_uuids</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_project_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">keywords</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;list of keywords for picture&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;keywords&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;name / title of picture&quot;&quot;&quot;</span>
<span class="c1"># if user sets then deletes title, Photos sets it to empty string in DB instead of NULL</span>
<span class="c1"># in this case, return None so result is the same as if title had never been set (which returns NULL)</span>
<span class="c1"># issue #512</span>
<span class="n">title</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span>
<span class="n">title</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">if</span> <span class="n">title</span> <span class="o">==</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="n">title</span>
<span class="k">return</span> <span class="n">title</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;UUID of picture&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ismissing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns true if photo is missing from disk (which means it&#39;s not been downloaded from iCloud)</span>
<span class="sd"> NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos</span>
<span class="sd"> do not immediately get written to disk. In particular, I&#39;ve noticed that downloading</span>
<span class="sd"> an image from the cloud does not force the database to be updated until something else</span>
<span class="sd"> e.g. an edit, keyword, etc. occurs forcing a database synch</span>
<span class="sd"> The exact process / timing is a mystery to be but be aware that if some photos were recently</span>
<span class="sd"> downloaded from cloud to local storate their status in the database might still show</span>
<span class="sd"> isMissing = 1</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;isMissing&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hasadjustments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;True if picture has adjustments / edits&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hasAdjustments&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">adjustments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns AdjustmentsInfo class for adjustment data or None if no adjustments; Photos 5+ only&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="n">plist_file</span> <span class="o">=</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">library</span><span class="p">)</span>
<span class="o">/</span> <span class="s2">&quot;resources&quot;</span>
<span class="o">/</span> <span class="s2">&quot;renders&quot;</span>
<span class="o">/</span> <span class="n">directory</span>
<span class="o">/</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">.plist&quot;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">plist_file</span><span class="o">.</span><span class="n">is_file</span><span class="p">():</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span> <span class="o">=</span> <span class="n">AdjustmentsInfo</span><span class="p">(</span><span class="n">plist_file</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">external_edit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if picture was edited outside of Photos using external editor&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;adjustmentFormatID&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;com.apple.Photos.externalEdit&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">favorite</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;True if picture is marked as favorite&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;favorite&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hidden</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;True if picture is hidden&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hidden&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">visible</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;True if picture is visble&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;visible&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">intrash</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;True if picture is in trash (&#39;Recently Deleted&#39; folder)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;intrash&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">date_trashed</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Date asset was placed in the trash or None&quot;&quot;&quot;</span>
<span class="c1"># TODO: add add_timezone(dt, offset_seconds) to datetime_utils</span>
<span class="c1"># also update date_modified</span>
<span class="n">trasheddate</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;trasheddate&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">trasheddate</span><span class="p">:</span>
<span class="n">seconds</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span> <span class="ow">or</span> <span class="mi">0</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="k">return</span> <span class="n">trasheddate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">date_added</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Date photo was added to the database&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_date_added</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">added_date</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;added_date&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">added_date</span><span class="p">:</span>
<span class="n">seconds</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span> <span class="ow">or</span> <span class="mi">0</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_date_added</span> <span class="o">=</span> <span class="n">added_date</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_date_added</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_date_added</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">location</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns (latitude, longitude) as float in degrees or None&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_latitude</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_longitude</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns True if photos is in a shared iCloud album otherwise false</span>
<span class="sd"> Only valid on Photos 5; returns None on older versions&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;shared&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uti</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns Uniform Type Identifier (UTI) for the image</span>
<span class="sd"> for example: public.jpeg or com.apple.quicktime-movie</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;UTI_edited&quot;</span><span class="p">]</span>
<span class="k">elif</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span>
<span class="p">):</span>
<span class="c1"># return UTI of the non-raw image to match Photos 5+ behavior</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;UTI&quot;</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;UTI&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uti_original</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns Uniform Type Identifier (UTI) for the original image</span>
<span class="sd"> for example: public.jpeg or com.apple.quicktime-movie</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;has_raw&quot;</span><span class="p">]:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">][</span><span class="s2">&quot;UTI&quot;</span><span class="p">]</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
<span class="c1"># TODO: need reliable way to get original UTI for shared</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&gt;=</span> <span class="mi">7</span><span class="p">:</span>
<span class="c1"># Monterey+</span>
<span class="c1"># there are some cases with UTI_original is None (photo imported with no extension) so fallback to UTI and hope it&#39;s right</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">get_uti_for_extension</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="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span><span class="p">)</span>
<span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;UTI_original&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uti_original</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uti_edited</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns Uniform Type Identifier (UTI) for the edited image</span>
<span class="sd"> if the photo has been edited, otherwise None;</span>
<span class="sd"> for example: public.jpeg</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&gt;=</span> <span class="n">_PHOTOS_5_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;UTI_edited&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">uti_raw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns Uniform Type Identifier (UTI) for the RAW image if there is one</span>
<span class="sd"> for example: com.canon.cr2-raw-image</span>
<span class="sd"> Returns None if no associated RAW image</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&lt;</span> <span class="mi">7</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;UTI_raw&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">rawpath</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_raw</span><span class="p">:</span>
<span class="k">return</span> <span class="n">get_uti_for_extension</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">rawpath</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ismovie</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if file is a movie, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="n">_MOVIE_TYPE</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">isphoto</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if file is an image, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;type&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="n">_PHOTO_TYPE</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">incloud</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is cloud asset and is synched to cloud</span>
<span class="sd"> False if photo is cloud asset and not yet synched to cloud</span>
<span class="sd"> None if photo is not cloud asset</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;incloud&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">iscloudasset</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a cloud asset (in an iCloud library),</span>
<span class="sd"> otherwise False</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span>
<span class="kc">True</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudLibraryState&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudLibraryState&quot;</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span>
<span class="k">else</span> <span class="kc">False</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">True</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudAssetGUID&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">False</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">isreference</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a reference (not copied to the Photos library), otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;isreference&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is part of a Burst photo set, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burst&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_selected</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burstPickType&quot;</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">BURST_SELECTED</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_key</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a burst photo and is the key image for the burst set (the image that Photos shows on top of the burst stack), otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burstPickType&quot;</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">BURST_KEY</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_default_pick</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a burst image and is the photo that Photos selected as the default image for the burst set, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burstPickType&quot;</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">BURST_DEFAULT_PICK</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">burst_photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;If photo is a burst photo, returns list of PhotoInfo objects</span>
<span class="sd"> that are part of the same burst photo set; otherwise returns empty list.</span>
<span class="sd"> self is not included in the returned list&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burst&quot;</span><span class="p">]:</span>
<span class="n">burst_uuid</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;burstUUID&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="p">[</span>
<span class="n">PhotoInfo</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">u</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">u</span><span class="p">])</span>
<span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos_burst</span><span class="p">[</span><span class="n">burst_uuid</span><span class="p">]</span>
<span class="k">if</span> <span class="n">u</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span>
<span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a live photo, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;live_photo&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">path_live_photo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns path to the associated video file for a live photo</span>
<span class="sd"> If photo is not a live photo, returns None</span>
<span class="sd"> If photo is missing, returns None&quot;&quot;&quot;</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="n">live_model_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;live_model_id&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">live_model_id</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;missing live_model_id: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">live_model_id</span><span class="p">)</span>
<span class="n">library_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">library_path</span>
<span class="n">photopath</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">library_path</span><span class="p">,</span>
<span class="s2">&quot;resources&quot;</span><span class="p">,</span>
<span class="s2">&quot;media&quot;</span><span class="p">,</span>
<span class="s2">&quot;master&quot;</span><span class="p">,</span>
<span class="n">folder_id</span><span class="p">,</span>
<span class="n">nn_id</span><span class="p">,</span>
<span class="sa">f</span><span class="s2">&quot;jpegvideocomplement_</span><span class="si">{</span><span class="n">file_id</span><span class="si">}</span><span class="s2">.mov&quot;</span><span class="p">,</span>
<span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># In testing, I&#39;ve seen occasional missing movie for live photo</span>
<span class="c1"># These appear to be valid -- e.g. live component hasn&#39;t been downloaded from iCloud</span>
<span class="c1"># photos 4 has &quot;isOnDisk&quot; column we could check</span>
<span class="c1"># or could do the actual check with &quot;isfile&quot;</span>
<span class="c1"># TODO: should this be a warning or debug?</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">elif</span> <span class="bp">self</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">path</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="n">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="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filename</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_3.mov&quot;</span><span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">photopath</span><span class="p">)</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">isfile</span><span class="p">(</span><span class="n">photopath</span><span class="p">):</span>
<span class="c1"># In testing, I&#39;ve seen occasional missing movie for live photo</span>
<span class="c1"># these appear to be valid -- e.g. video component not yet downloaded from iCloud</span>
<span class="c1"># TODO: should this be a warning or debug?</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">path_derivatives</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_4</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_5_shared</span><span class="p">()</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="n">derivative_path</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">&quot;resources/derivatives/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">derivative_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">*.*&quot;</span><span class="p">))</span>
<span class="c1"># previews may be missing from derivatives path</span>
<span class="c1"># there are what appear to be low res thumbnails in the &quot;masters&quot; subfolder</span>
<span class="n">thumb_path</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
<span class="o">/</span> <span class="sa">f</span><span class="s2">&quot;resources/derivatives/masters/</span><span class="si">{</span><span class="n">directory</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="si">}</span><span class="s2">_4_5005_c.jpeg&quot;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">thumb_path</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
<span class="n">files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">thumb_path</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
<span class="c1"># return list of filename but skip .THM files (these are actually low-res thumbnails in JPEG format but with .THM extension)</span>
<span class="n">derivatives</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span> <span class="k">if</span> <span class="n">filename</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="s2">&quot;.THM&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">derivatives</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">&quot;.mov&quot;</span><span class="p">):</span>
<span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">return</span> <span class="n">derivatives</span>
<span class="k">def</span> <span class="nf">_path_derivatives_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return paths to all derivative (preview) files for Photos &lt;= 4&quot;&quot;&quot;</span>
<span class="n">modelid</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;modelID&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">modelid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span><span class="p">,</span> <span class="n">nn_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">modelid</span><span class="p">)</span>
<span class="n">derivatives_root</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
<span class="o">/</span> <span class="sa">f</span><span class="s2">&quot;resources/proxies/derivatives/</span><span class="si">{</span><span class="n">folder_id</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="n">derivatives_path</span> <span class="o">=</span> <span class="n">derivatives_root</span> <span class="o">/</span> <span class="n">nn_id</span> <span class="o">/</span> <span class="n">file_id</span>
<span class="k">if</span> <span class="n">derivatives_path</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
<span class="n">files</span> <span class="o">=</span> <span class="n">derivatives_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&quot;*&quot;</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span><span class="p">]</span>
<span class="c1"># didn&#39;t find derivatives path</span>
<span class="k">for</span> <span class="n">subdir</span> <span class="ow">in</span> <span class="n">derivatives_root</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&quot;*&quot;</span><span class="p">):</span>
<span class="k">if</span> <span class="n">subdir</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
<span class="n">derivatives_path</span> <span class="o">=</span> <span class="n">derivatives_root</span> <span class="o">/</span> <span class="n">subdir</span> <span class="o">/</span> <span class="n">file_id</span>
<span class="k">if</span> <span class="n">derivatives_path</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
<span class="n">files</span> <span class="o">=</span> <span class="n">derivatives_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&quot;*&quot;</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span><span class="p">]</span>
<span class="c1"># didn&#39;t find a derivatives path</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">def</span> <span class="nf">_path_derivatives_5_shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return paths to all derivative (preview) files for shared iCloud photos in Photos &gt;= 5&quot;&quot;&quot;</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="c1"># only 1 derivative for shared photos and it&#39;s called &#39;UUID_4_5005_c.jpeg&#39;</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">_PHOTOS_8_SHARED_DERIVATIVE_PATH</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">&gt;=</span> <span class="mi">8</span>
<span class="k">else</span> <span class="n">_PHOTOS_5_SHARED_DERIVATIVE_PATH</span>
<span class="p">)</span>
<span class="n">derivative_path</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
<span class="o">/</span> <span class="n">derivative_path</span>
<span class="o">/</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">directory</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="si">}</span><span class="s2">_4_5005_c.jpeg&quot;</span>
<span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">derivative_path</span><span class="p">)]</span> <span class="k">if</span> <span class="n">derivative_path</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="k">else</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">panorama</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a panorama, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;panorama&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">slow_mo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a slow motion video, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;slow_mo&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">time_lapse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a time lapse video, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;time_lapse&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hdr</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is an HDR photo, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hdr&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">screenshot</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is an HDR photo, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;screenshot&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">portrait</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a portrait, otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;portrait&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">selfie</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is a selfie (front facing camera), otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;selfie&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">place</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns PlaceInfo object containing reverse geolocation info&quot;&quot;&quot;</span>
<span class="c1"># implementation note: doesn&#39;t create the PlaceInfo object until requested</span>
<span class="c1"># then memoizes the object in self._place to avoid recreating the object</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="c1"># pylint: disable=access-member-before-definition</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;placeNames&quot;</span><span class="p">]:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="o">=</span> <span class="n">PlaceInfo4</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;placeNames&quot;</span><span class="p">],</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;countryCode&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="c1"># pylint: disable=access-member-before-definition</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;reverse_geolocation&quot;</span><span class="p">]:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="o">=</span> <span class="n">PlaceInfo5</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;reverse_geolocation&quot;</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">has_raw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns True if photo has an associated raw image (that is, it&#39;s a RAW+JPEG pair), otherwise False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;has_raw&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">israw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns True if photo is a raw image. For images with an associated RAW+JPEG pair, see has_raw&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;raw-image&quot;</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span> <span class="k">else</span> <span class="kc">False</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">raw_original</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns True if associated raw image and the raw image is selected in Photos</span>
<span class="sd"> via &quot;Use RAW as Original &quot;</span>
<span class="sd"> otherwise returns False&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;raw_is_original&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">height</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns height of the current photo version in pixels&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;height&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">width</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns width of the current photo version in pixels&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;width&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">orientation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns EXIF orientation of the current photo version as int or 0 if current orientation cannot be determined&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;orientation&quot;</span><span class="p">]</span>
<span class="c1"># For Photos 5+, try to get the adjusted orientation</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;orientation&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="o">.</span><span class="n">adj_orientation</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># can&#39;t reliably determine orientation for edited photo if adjustmentinfo not available</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">original_height</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns height of the original photo version in pixels&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;original_height&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">original_width</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns width of the original photo version in pixels&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;original_width&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">original_orientation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns EXIF orientation of the original photo version as int&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;original_orientation&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">original_filesize</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns filesize of original photo in bytes as int&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;original_filesize&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">duplicates</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;return list of PhotoInfo objects for possible duplicates (matching signature of original size, date, height, width) or empty list if no matching duplicates&quot;&quot;&quot;</span>
<span class="n">signature</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_duplicate_signature</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="n">duplicates</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_signatures</span><span class="p">[</span><span class="n">signature</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">uuid</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
<span class="c1"># found a possible duplicate</span>
<span class="n">duplicates</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">get_photo</span><span class="p">(</span><span class="n">uuid</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># don&#39;t expect this to happen as the signature should be in db</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">&quot;Did not find signature for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2"> in _db_signatures&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">duplicates</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">owner</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return name of photo owner for shared photos (Photos 5+ only), or None if not shared&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_owner</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">personid</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_owner</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">personid</span><span class="p">][</span><span class="s2">&quot;full_name&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="n">personid</span>
<span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_owner</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_owner</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">score</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Computed score information for a photo</span>
<span class="sd"> Returns:</span>
<span class="sd"> ScoreInfo instance</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_scoreinfo</span> <span class="c1"># pylint: disable=access-member-before-definition</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">scores</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_scoreinfo_uuid</span><span class="p">[</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">_scoreinfo</span> <span class="o">=</span> <span class="n">ScoreInfo</span><span class="p">(</span>
<span class="n">overall</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;overall_aesthetic&quot;</span><span class="p">],</span>
<span class="n">curation</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;curation&quot;</span><span class="p">],</span>
<span class="n">promotion</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;promotion&quot;</span><span class="p">],</span>
<span class="n">highlight_visibility</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;highlight_visibility&quot;</span><span class="p">],</span>
<span class="n">behavioral</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;behavioral&quot;</span><span class="p">],</span>
<span class="n">failure</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;failure&quot;</span><span class="p">],</span>
<span class="n">harmonious_color</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;harmonious_color&quot;</span><span class="p">],</span>
<span class="n">immersiveness</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;immersiveness&quot;</span><span class="p">],</span>
<span class="n">interaction</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;interaction&quot;</span><span class="p">],</span>
<span class="n">interesting_subject</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;interesting_subject&quot;</span><span class="p">],</span>
<span class="n">intrusive_object_presence</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;intrusive_object_presence&quot;</span><span class="p">],</span>
<span class="n">lively_color</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;lively_color&quot;</span><span class="p">],</span>
<span class="n">low_light</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;low_light&quot;</span><span class="p">],</span>
<span class="n">noise</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;noise&quot;</span><span class="p">],</span>
<span class="n">pleasant_camera_tilt</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_camera_tilt&quot;</span><span class="p">],</span>
<span class="n">pleasant_composition</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_composition&quot;</span><span class="p">],</span>
<span class="n">pleasant_lighting</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_lighting&quot;</span><span class="p">],</span>
<span class="n">pleasant_pattern</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_pattern&quot;</span><span class="p">],</span>
<span class="n">pleasant_perspective</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_perspective&quot;</span><span class="p">],</span>
<span class="n">pleasant_post_processing</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_post_processing&quot;</span><span class="p">],</span>
<span class="n">pleasant_reflection</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_reflection&quot;</span><span class="p">],</span>
<span class="n">pleasant_symmetry</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;pleasant_symmetry&quot;</span><span class="p">],</span>
<span class="n">sharply_focused_subject</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;sharply_focused_subject&quot;</span><span class="p">],</span>
<span class="n">tastefully_blurred</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;tastefully_blurred&quot;</span><span class="p">],</span>
<span class="n">well_chosen_subject</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;well_chosen_subject&quot;</span><span class="p">],</span>
<span class="n">well_framed_subject</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;well_framed_subject&quot;</span><span class="p">],</span>
<span class="n">well_timed_shot</span><span class="o">=</span><span class="n">scores</span><span class="p">[</span><span class="s2">&quot;well_timed_shot&quot;</span><span class="p">],</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_scoreinfo</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_scoreinfo</span> <span class="o">=</span> <span class="n">ScoreInfo</span><span class="p">(</span>
<span class="n">overall</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">curation</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">promotion</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">highlight_visibility</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">behavioral</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">failure</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">harmonious_color</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">immersiveness</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">interaction</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">interesting_subject</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">intrusive_object_presence</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">lively_color</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">low_light</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">noise</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_camera_tilt</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_composition</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_lighting</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_pattern</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_perspective</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_post_processing</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_reflection</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">pleasant_symmetry</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">sharply_focused_subject</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">tastefully_blurred</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">well_chosen_subject</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">well_framed_subject</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="n">well_timed_shot</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_scoreinfo</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">search_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns SearchInfo object for photo</span>
<span class="sd"> only valid on Photos 5, on older libraries, returns None</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="c1"># memoize SearchInfo object</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search_info</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_search_info</span> <span class="o">=</span> <span class="n">SearchInfo</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">search_info_normalized</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns SearchInfo object for photo that produces normalized results</span>
<span class="sd"> only valid on Photos 5, on older libraries, returns None</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="c1"># memoize SearchInfo object</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search_info_normalized</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_search_info_normalized</span> <span class="o">=</span> <span class="n">SearchInfo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">normalized</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search_info_normalized</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">labels</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of labels applied to photo by Photos image categorization</span>
<span class="sd"> only valid on Photos 5, on older libraries returns empty list</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">labels</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">labels_normalized</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns normalized list of labels applied to photo by Photos image categorization</span>
<span class="sd"> only valid on Photos 5, on older libraries returns empty list</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span><span class="o">.</span><span class="n">labels</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">comments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns list of Comment objects for any comments on the photo (sorted by date)&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">likes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns list of Like objects for any likes on the photo (sorted by date)&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">exif_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns an ExifInfo object with the EXIF data for photo</span>
<span class="sd"> Note: the returned EXIF data is the data Photos stores in the database on import;</span>
<span class="sd"> ExifInfo does not provide access to the EXIF info in the actual image file</span>
<span class="sd"> Some or all of the fields may be None</span>
<span class="sd"> Only valid for Photos 5; on earlier database returns None</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</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">_db</span><span class="o">.</span><span class="n">_db_exifinfo_uuid</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">]</span>
<span class="n">exif_info</span> <span class="o">=</span> <span class="n">ExifInfo</span><span class="p">(</span>
<span class="n">iso</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZISO&quot;</span><span class="p">],</span>
<span class="n">flash_fired</span><span class="o">=</span><span class="kc">True</span> <span class="k">if</span> <span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZFLASHFIRED&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="kc">False</span><span class="p">,</span>
<span class="n">metering_mode</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZMETERINGMODE&quot;</span><span class="p">],</span>
<span class="n">sample_rate</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZSAMPLERATE&quot;</span><span class="p">],</span>
<span class="n">track_format</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZTRACKFORMAT&quot;</span><span class="p">],</span>
<span class="n">white_balance</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZWHITEBALANCE&quot;</span><span class="p">],</span>
<span class="n">aperture</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZAPERTURE&quot;</span><span class="p">],</span>
<span class="n">bit_rate</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZBITRATE&quot;</span><span class="p">],</span>
<span class="n">duration</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZDURATION&quot;</span><span class="p">],</span>
<span class="n">exposure_bias</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZEXPOSUREBIAS&quot;</span><span class="p">],</span>
<span class="n">focal_length</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZFOCALLENGTH&quot;</span><span class="p">],</span>
<span class="n">fps</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZFPS&quot;</span><span class="p">],</span>
<span class="n">latitude</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZLATITUDE&quot;</span><span class="p">],</span>
<span class="n">longitude</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZLONGITUDE&quot;</span><span class="p">],</span>
<span class="n">shutter_speed</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZSHUTTERSPEED&quot;</span><span class="p">],</span>
<span class="n">camera_make</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZCAMERAMAKE&quot;</span><span class="p">],</span>
<span class="n">camera_model</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZCAMERAMODEL&quot;</span><span class="p">],</span>
<span class="n">codec</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZCODEC&quot;</span><span class="p">],</span>
<span class="n">lens_model</span><span class="o">=</span><span class="n">exif</span><span class="p">[</span><span class="s2">&quot;ZLENSMODEL&quot;</span><span class="p">],</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Could not find exif record for uuid </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exif_info</span> <span class="o">=</span> <span class="n">ExifInfo</span><span class="p">(</span>
<span class="n">iso</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">flash_fired</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">metering_mode</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sample_rate</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">track_format</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">white_balance</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">aperture</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">bit_rate</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">duration</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exposure_bias</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">focal_length</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">fps</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">latitude</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">longitude</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">shutter_speed</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">camera_make</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">camera_model</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">codec</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">lens_model</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">exif_info</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns a ExifToolCaching (read-only instance of ExifTool) object for the photo.</span>
<span class="sd"> Requires that exiftool (https://exiftool.org/) be installed</span>
<span class="sd"> If exiftool not installed, logs warning and returns None</span>
<span class="sd"> If photo path is missing, returns None</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># return the memoized instance if it exists</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">exiftool_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_exiftool_path</span> <span class="ow">or</span> <span class="n">get_exiftool_path</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">):</span>
<span class="n">exiftool</span> <span class="o">=</span> <span class="n">ExifToolCaching</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool_path</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="kc">None</span>
<span class="k">except</span> <span class="ne">FileNotFoundError</span><span class="p">:</span>
<span class="c1"># get_exiftool_path raises FileNotFoundError if exiftool not found</span>
<span class="n">exiftool</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
<span class="s2">&quot;exiftool not in path; download and install from https://exiftool.org/&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span> <span class="o">=</span> <span class="n">exiftool</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns a unique digest of the photo&#39;s properties and metadata;</span>
<span class="sd"> useful for detecting changes in any property/metadata of the photo&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_json_hexdigest</span><span class="p">())</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_metadata</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Returns contents of ZCLOUDMASTERMEDIAMETADATA as dict; Photos 5+ only&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{}</span>
<span class="c1"># This is a large blob of data so don&#39;t load it unless requested</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">&quot;ASSET&quot;</span><span class="p">]</span>
<span class="n">sql_cloud_metadata</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> SELECT ZCLOUDMASTERMEDIAMETADATA.ZDATA</span>
<span class="s2"> FROM ZCLOUDMASTERMEDIAMETADATA</span>
<span class="s2"> JOIN ZCLOUDMASTER ON ZCLOUDMASTER.Z_PK = ZCLOUDMASTERMEDIAMETADATA.ZCLOUDMASTER</span>
<span class="s2"> JOIN </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"> on </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZMASTER = ZCLOUDMASTER.Z_PK</span>
<span class="s2"> WHERE </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID = ?</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="n">_</span><span class="p">,</span> <span class="n">cursor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">get_db_connection</span><span class="p">()</span>
<span class="n">metadata</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="n">results</span> <span class="o">:=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">sql_cloud_metadata</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,))</span><span class="o">.</span><span class="n">fetchone</span><span class="p">():</span>
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">suppress</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
<span class="n">metadata</span> <span class="o">=</span> <span class="n">plistlib</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">return</span> <span class="n">metadata</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_guid</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the GUID of the photo in iCloud (Photos 5+ only)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudMasterGUID&quot;</span><span class="p">]</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">cloud_owner_hashed_id</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the hashed iCloud ID of the owner of the shared photo (Photos 5+ only)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">]</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">fingerprint</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns fingerprint of original photo as a string&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;masterFingerprint&quot;</span><span class="p">]</span>
<div class="viewcode-block" id="PhotoInfo.detected_text"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.detected_text">[docs]</a> <span class="k">def</span> <span class="nf">detected_text</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">confidence_threshold</span><span class="o">=</span><span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Detects text in photo and returns lists of results as (detected text, confidence)</span>
<span class="sd"> confidence_threshold: float between 0.0 and 1.0. If text detection confidence is below this threshold,</span>
<span class="sd"> text will not be returned. Default is TEXT_DETECTION_CONFIDENCE_THRESHOLD</span>
<span class="sd"> If photo is edited, uses the edited photo, otherwise the original; falls back to the preview image if neither edited or original is available</span>
<span class="sd"> Returns: list of (detected text, confidence) tuples</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">AttributeError</span><span class="p">,</span> <span class="ne">KeyError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="ne">AttributeError</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_detected_text_cache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_detected_text</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">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error detecting text in photo </span><span class="si">{</span><span class="bp">self</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">&quot;</span><span class="p">)</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">confidence</span><span class="p">)</span>
<span class="k">for</span> <span class="n">text</span><span class="p">,</span> <span class="n">confidence</span> <span class="ow">in</span> <span class="n">detected_text</span>
<span class="k">if</span> <span class="n">confidence</span> <span class="o">&gt;=</span> <span class="n">confidence_threshold</span>
<span class="p">]</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span></div>
<span class="k">def</span> <span class="nf">_detected_text</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;detect text in photo, either from cached extended attribute or by attempting text detection&quot;&quot;&quot;</span>
<span class="n">path</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span>
<span class="p">)</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">path</span> <span class="ow">or</span> <span class="bp">self</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="bp">self</span><span class="o">.</span><span class="n">path_derivatives</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="n">md</span> <span class="o">=</span> <span class="n">OSXMetaData</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">decoder</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Decode value from JSON&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">val</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">))</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">md</span><span class="o">.</span><span class="n">get_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">decode</span><span class="o">=</span><span class="n">decoder</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">detected_text</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">orientation</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">orientation</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">detect_text</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">orientation</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">encoder</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Encode value as JSON&quot;&quot;&quot;</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">return</span> <span class="n">val</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">md</span><span class="o">.</span><span class="n">set_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">detected_text</span><span class="p">,</span> <span class="n">encode</span><span class="o">=</span><span class="n">encoder</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">detected_text</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">_longitude</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns longitude, in degrees&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;longitude&quot;</span><span class="p">]</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">_latitude</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns latitude, in degrees&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;latitude&quot;</span><span class="p">]</span>
<div class="viewcode-block" id="PhotoInfo.render_template"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.render_template">[docs]</a> <span class="k">def</span> <span class="nf">render_template</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">template_str</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">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="p">):</span>
<span class="sd">&quot;&quot;&quot;Renders a template string for PhotoInfo instance using PhotoTemplate</span>
<span class="sd"> Args:</span>
<span class="sd"> template_str: a template string with fields to render</span>
<span class="sd"> options: a RenderOptions instance</span>
<span class="sd"> Returns:</span>
<span class="sd"> ([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span> <span class="ow">or</span> <span class="n">RenderOptions</span><span class="p">()</span>
<span class="n">template</span> <span class="o">=</span> <span class="n">PhotoTemplate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exiftool_path</span><span class="o">=</span><span class="bp">self</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="k">return</span> <span class="n">template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">template_str</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span></div>
<div class="viewcode-block" id="PhotoInfo.export"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.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">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">raw_photo</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">export_as_hardlink</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="n">increment</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">sidecar_json</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">sidecar_exiftool</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">sidecar_xmp</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">use_photos_export</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">exiftool</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">use_albums_as_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">use_persons_as_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">keyword_template</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">description_template</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">render_options</span><span class="p">:</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="p">,</span>
<span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Export a 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"> export will print a warning but will export the photo using the</span>
<span class="sd"> incorrect file extension (unless use_photos_export is true, in which case export will</span>
<span class="sd"> use the extension provided by Photos upon export; in this case, an incorrect extension is</span>
<span class="sd"> silently ignored).</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"> edited: (boolean, default=False); if True will export the edited version of the photo, otherwise exports the original version</span>
<span class="sd"> (or raise exception if no edited version)</span>
<span class="sd"> live_photo: (boolean, default=False); if True, will also export the associated .mov for live photos</span>
<span class="sd"> raw_photo: (boolean, default=False); if True, will also export the associated RAW photo</span>
<span class="sd"> export_as_hardlink: (boolean, default=False); if True, will hardlink files instead of copying them</span>
<span class="sd"> overwrite: (boolean, default=False); if True will overwrite files if they already exist</span>
<span class="sd"> increment: (boolean, default=True); if True, will increment file name until a non-existant name is found</span>
<span class="sd"> if overwrite=False and increment=False, export will fail if destination file already exists</span>
<span class="sd"> sidecar_json: if set will write a json sidecar with data in format readable by exiftool</span>
<span class="sd"> sidecar filename will be dest/filename.json; 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</span>
<span class="sd"> sidecar filename will be dest/filename.json; 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</span>
<span class="sd"> sidecar filename will be dest/filename.xmp</span>
<span class="sd"> use_photos_export: (boolean, default=False); if True will attempt to export photo via applescript interaction with Photos</span>
<span class="sd"> timeout: (int, default=120) timeout in seconds used with use_photos_export</span>
<span class="sd"> exiftool: (boolean, default = False); if True, will use exiftool to write metadata to export file</span>
<span class="sd"> returns list of full paths to the exported files</span>
<span class="sd"> use_albums_as_keywords: (boolean, default = False); if True, will include album names in keywords</span>
<span class="sd"> when exporting metadata with exiftool or sidecar</span>
<span class="sd"> use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords</span>
<span class="sd"> when exporting metadata with exiftool or sidecar</span>
<span class="sd"> keyword_template: (list of strings); list of template strings that will be rendered as used as keywords</span>
<span class="sd"> description_template: string; optional template string that will be rendered for use as photo description</span>
<span class="sd"> render_options: an optional osxphotos.phototemplate.RenderOptions instance with options to pass to template renderer</span>
<span class="sd"> Returns:</span>
<span class="sd"> list of photos exported</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">exporter</span> <span class="o">=</span> <span class="n">PhotoExporter</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="n">sidecar</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">sidecar_json</span><span class="p">:</span>
<span class="n">sidecar</span> <span class="o">|=</span> <span class="n">SIDECAR_JSON</span>
<span class="k">if</span> <span class="n">sidecar_exiftool</span><span class="p">:</span>
<span class="n">sidecar</span> <span class="o">|=</span> <span class="n">SIDECAR_EXIFTOOL</span>
<span class="k">if</span> <span class="n">sidecar_xmp</span><span class="p">:</span>
<span class="n">sidecar</span> <span class="o">|=</span> <span class="n">SIDECAR_XMP</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">filename</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">edited</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">original_name</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">original_filename</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</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">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">uti_edited</span> <span class="k">if</span> <span class="n">edited</span> <span class="ow">and</span> <span class="bp">self</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">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="sa">f</span><span class="s2">&quot;.</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">original_name</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_edited</span><span class="si">{</span><span class="n">ext</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">ExportOptions</span><span class="p">(</span>
<span class="n">description_template</span><span class="o">=</span><span class="n">description_template</span><span class="p">,</span>
<span class="n">edited</span><span class="o">=</span><span class="n">edited</span><span class="p">,</span>
<span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool</span><span class="p">,</span>
<span class="n">export_as_hardlink</span><span class="o">=</span><span class="n">export_as_hardlink</span><span class="p">,</span>
<span class="n">increment</span><span class="o">=</span><span class="n">increment</span><span class="p">,</span>
<span class="n">keyword_template</span><span class="o">=</span><span class="n">keyword_template</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">overwrite</span><span class="o">=</span><span class="n">overwrite</span><span class="p">,</span>
<span class="n">raw_photo</span><span class="o">=</span><span class="n">raw_photo</span><span class="p">,</span>
<span class="n">render_options</span><span class="o">=</span><span class="n">render_options</span><span class="p">,</span>
<span class="n">sidecar</span><span class="o">=</span><span class="n">sidecar</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">use_albums_as_keywords</span><span class="o">=</span><span class="n">use_albums_as_keywords</span><span class="p">,</span>
<span class="n">use_persons_as_keywords</span><span class="o">=</span><span class="n">use_persons_as_keywords</span><span class="p">,</span>
<span class="n">use_photos_export</span><span class="o">=</span><span class="n">use_photos_export</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">exporter</span><span class="o">.</span><span class="n">export</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="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">return</span> <span class="n">results</span><span class="o">.</span><span class="n">exported</span></div>
<span class="k">def</span> <span class="nf">_get_album_uuids</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">project</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return list of album UUIDs this photo is found in</span>
<span class="sd"> Filters out albums in the trash and any special album types</span>
<span class="sd"> if project is True, returns special &quot;My Project&quot; albums (e.g. cards, calendars, slideshows)</span>
<span class="sd"> Returns: list of album UUIDs</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
<span class="n">album_kind</span> <span class="o">=</span> <span class="p">[</span><span class="n">_PHOTOS_4_ALBUM_KIND</span><span class="p">]</span>
<span class="n">album_type</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">[</span><span class="n">_PHOTOS_4_ALBUM_TYPE_PROJECT</span><span class="p">,</span> <span class="n">_PHOTOS_4_ALBUM_TYPE_SLIDESHOW</span><span class="p">]</span>
<span class="k">if</span> <span class="n">project</span>
<span class="k">else</span> <span class="p">[</span><span class="n">_PHOTOS_4_ALBUM_TYPE_ALBUM</span><span class="p">]</span>
<span class="p">)</span>
<span class="n">album_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;albums&quot;</span><span class="p">]:</span>
<span class="n">detail</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbalbum_details</span><span class="p">[</span><span class="n">album</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span>
<span class="n">detail</span><span class="p">[</span><span class="s2">&quot;kind&quot;</span><span class="p">]</span> <span class="ow">in</span> <span class="n">album_kind</span>
<span class="ow">and</span> <span class="n">detail</span><span class="p">[</span><span class="s2">&quot;albumType&quot;</span><span class="p">]</span> <span class="ow">in</span> <span class="n">album_type</span>
<span class="ow">and</span> <span class="ow">not</span> <span class="n">detail</span><span class="p">[</span><span class="s2">&quot;intrash&quot;</span><span class="p">]</span>
<span class="ow">and</span> <span class="n">detail</span><span class="p">[</span><span class="s2">&quot;folderUuid&quot;</span><span class="p">]</span> <span class="o">!=</span> <span class="n">_PHOTOS_4_ROOT_FOLDER</span>
<span class="c1"># in Photos &lt;= 4, special albums like &quot;printAlbum&quot; have kind _PHOTOS_4_ALBUM_KIND</span>
<span class="c1"># but should not be listed here; they can be distinguished by looking</span>
<span class="c1"># for folderUuid of _PHOTOS_4_ROOT_FOLDER as opposed to _PHOTOS_4_TOP_LEVEL_ALBUM</span>
<span class="p">):</span>
<span class="n">album_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">album</span><span class="p">)</span>
<span class="k">return</span> <span class="n">album_list</span>
<span class="c1"># Photos 5+</span>
<span class="n">album_kind</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">[</span><span class="n">_PHOTOS_5_PROJECT_ALBUM_KIND</span><span class="p">]</span>
<span class="k">if</span> <span class="n">project</span>
<span class="k">else</span> <span class="p">[</span><span class="n">_PHOTOS_5_SHARED_ALBUM_KIND</span><span class="p">,</span> <span class="n">_PHOTOS_5_ALBUM_KIND</span><span class="p">]</span>
<span class="p">)</span>
<span class="n">album_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;albums&quot;</span><span class="p">]:</span>
<span class="n">detail</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbalbum_details</span><span class="p">[</span><span class="n">album</span><span class="p">]</span>
<span class="k">if</span> <span class="n">detail</span><span class="p">[</span><span class="s2">&quot;kind&quot;</span><span class="p">]</span> <span class="ow">in</span> <span class="n">album_kind</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">detail</span><span class="p">[</span><span class="s2">&quot;intrash&quot;</span><span class="p">]:</span>
<span class="n">album_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">album</span><span class="p">)</span>
<span class="k">return</span> <span class="n">album_list</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;osxphotos.</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">(db=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="si">}</span><span class="s2">, uuid=&#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">&#39;, info=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="si">}</span><span class="s2">)&quot;</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="sd">&quot;&quot;&quot;string representation of PhotoInfo object&quot;&quot;&quot;</span>
<span class="n">date_iso</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">date_modified_iso</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span> <span class="k">else</span> <span class="kc">None</span>
<span class="p">)</span>
<span class="n">exif</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">score</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">score</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">score</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span>
<span class="s2">&quot;original_filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">,</span>
<span class="s2">&quot;date&quot;</span><span class="p">:</span> <span class="n">date_iso</span><span class="p">,</span>
<span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;keywords&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">,</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">,</span>
<span class="s2">&quot;persons&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">,</span>
<span class="s2">&quot;path&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">,</span>
<span class="s2">&quot;ismissing&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">,</span>
<span class="s2">&quot;hasadjustments&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">,</span>
<span class="s2">&quot;external_edit&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">external_edit</span><span class="p">,</span>
<span class="s2">&quot;favorite&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">favorite</span><span class="p">,</span>
<span class="s2">&quot;hidden&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hidden</span><span class="p">,</span>
<span class="s2">&quot;latitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_latitude</span><span class="p">,</span>
<span class="s2">&quot;longitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_longitude</span><span class="p">,</span>
<span class="s2">&quot;path_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span><span class="p">,</span>
<span class="s2">&quot;shared&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">,</span>
<span class="s2">&quot;isphoto&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">,</span>
<span class="s2">&quot;ismovie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">,</span>
<span class="s2">&quot;uti&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span><span class="p">,</span>
<span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
<span class="s2">&quot;live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">,</span>
<span class="s2">&quot;path_live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_live_photo</span><span class="p">,</span>
<span class="s2">&quot;iscloudasset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">iscloudasset</span><span class="p">,</span>
<span class="s2">&quot;incloud&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">incloud</span><span class="p">,</span>
<span class="s2">&quot;date_modified&quot;</span><span class="p">:</span> <span class="n">date_modified_iso</span><span class="p">,</span>
<span class="s2">&quot;portrait&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">portrait</span><span class="p">,</span>
<span class="s2">&quot;screenshot&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">screenshot</span><span class="p">,</span>
<span class="s2">&quot;slow_mo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">slow_mo</span><span class="p">,</span>
<span class="s2">&quot;time_lapse&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">time_lapse</span><span class="p">,</span>
<span class="s2">&quot;hdr&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hdr</span><span class="p">,</span>
<span class="s2">&quot;selfie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">selfie</span><span class="p">,</span>
<span class="s2">&quot;panorama&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">panorama</span><span class="p">,</span>
<span class="s2">&quot;has_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span><span class="p">,</span>
<span class="s2">&quot;uti_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_raw</span><span class="p">,</span>
<span class="s2">&quot;path_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_raw</span><span class="p">,</span>
<span class="s2">&quot;place&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span><span class="p">,</span>
<span class="s2">&quot;exif&quot;</span><span class="p">:</span> <span class="n">exif</span><span class="p">,</span>
<span class="s2">&quot;score&quot;</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span>
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">intrash</span><span class="p">,</span>
<span class="s2">&quot;height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="p">,</span>
<span class="s2">&quot;width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="p">,</span>
<span class="s2">&quot;orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">orientation</span><span class="p">,</span>
<span class="s2">&quot;original_height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_height</span><span class="p">,</span>
<span class="s2">&quot;original_width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_width</span><span class="p">,</span>
<span class="s2">&quot;original_orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_orientation</span><span class="p">,</span>
<span class="s2">&quot;original_filesize&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filesize</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">yaml</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<div class="viewcode-block" id="PhotoInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.asdict">[docs]</a> <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="sd">&quot;&quot;&quot;return dict representation&quot;&quot;&quot;</span>
<span class="n">adjustments</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">album_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">album</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">]</span>
<span class="n">burst_album_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_album_info</span><span class="p">]</span>
<span class="n">burst_photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</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">burst_photos</span><span class="p">]</span>
<span class="n">comments</span> <span class="o">=</span> <span class="p">[</span><span class="n">comment</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">comment</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">comments</span><span class="p">]</span>
<span class="n">exif_info</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">exif_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">face_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">face</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</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">face_info</span><span class="p">]</span>
<span class="n">folders</span> <span class="o">=</span> <span class="p">{</span><span class="n">album</span><span class="o">.</span><span class="n">title</span><span class="p">:</span> <span class="n">album</span><span class="o">.</span><span class="n">folder_names</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">}</span>
<span class="n">import_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">import_info</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">import_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">likes</span> <span class="o">=</span> <span class="p">[</span><span class="n">like</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">like</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">likes</span><span class="p">]</span>
<span class="n">person_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">asdict</span><span class="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">person_info</span><span class="p">]</span>
<span class="n">place</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">project_info</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">asdict</span><span class="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">project_info</span><span class="p">]</span>
<span class="n">score</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">score</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">score</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">search_info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">search_info_normalized</span> <span class="o">=</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span> <span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;adjustments&quot;</span><span class="p">:</span> <span class="n">adjustments</span><span class="p">,</span>
<span class="s2">&quot;album_info&quot;</span><span class="p">:</span> <span class="n">album_info</span><span class="p">,</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">,</span>
<span class="s2">&quot;burst_album_info&quot;</span><span class="p">:</span> <span class="n">burst_album_info</span><span class="p">,</span>
<span class="s2">&quot;burst_albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_albums</span><span class="p">,</span>
<span class="s2">&quot;burst_default_pick&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_default_pick</span><span class="p">,</span>
<span class="s2">&quot;burst_key&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_key</span><span class="p">,</span>
<span class="s2">&quot;burst_photos&quot;</span><span class="p">:</span> <span class="n">burst_photos</span><span class="p">,</span>
<span class="s2">&quot;burst_selected&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_selected</span><span class="p">,</span>
<span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
<span class="s2">&quot;cloud_guid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_guid</span><span class="p">,</span>
<span class="s2">&quot;cloud_metadata&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_metadata</span><span class="p">,</span>
<span class="s2">&quot;cloud_owner_hashed_id&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">cloud_owner_hashed_id</span><span class="p">,</span>
<span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="n">comments</span><span class="p">,</span>
<span class="s2">&quot;date_added&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_added</span><span class="p">,</span>
<span class="s2">&quot;date_modified&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span><span class="p">,</span>
<span class="s2">&quot;date_trashed&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date_trashed</span><span class="p">,</span>
<span class="s2">&quot;date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="p">,</span>
<span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span><span class="p">,</span>
<span class="s2">&quot;exif_info&quot;</span><span class="p">:</span> <span class="n">exif_info</span><span class="p">,</span>
<span class="s2">&quot;external_edit&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">external_edit</span><span class="p">,</span>
<span class="s2">&quot;face_info&quot;</span><span class="p">:</span> <span class="n">face_info</span><span class="p">,</span>
<span class="s2">&quot;favorite&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">favorite</span><span class="p">,</span>
<span class="s2">&quot;filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span>
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">fingerprint</span><span class="p">,</span>
<span class="s2">&quot;folders&quot;</span><span class="p">:</span> <span class="n">folders</span><span class="p">,</span>
<span class="s2">&quot;has_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">has_raw</span><span class="p">,</span>
<span class="s2">&quot;hasadjustments&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">,</span>
<span class="s2">&quot;hdr&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hdr</span><span class="p">,</span>
<span class="s2">&quot;height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</span><span class="p">,</span>
<span class="s2">&quot;hidden&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">hidden</span><span class="p">,</span>
<span class="s2">&quot;import_info&quot;</span><span class="p">:</span> <span class="n">import_info</span><span class="p">,</span>
<span class="s2">&quot;incloud&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">incloud</span><span class="p">,</span>
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">intrash</span><span class="p">,</span>
<span class="s2">&quot;iscloudasset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">iscloudasset</span><span class="p">,</span>
<span class="s2">&quot;ismissing&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">,</span>
<span class="s2">&quot;ismovie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">,</span>
<span class="s2">&quot;isphoto&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">,</span>
<span class="s2">&quot;israw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">israw</span><span class="p">,</span>
<span class="s2">&quot;isreference&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">isreference</span><span class="p">,</span>
<span class="s2">&quot;keywords&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">,</span>
<span class="s2">&quot;labels_normalized&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">labels_normalized</span><span class="p">,</span>
<span class="s2">&quot;labels&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">labels</span><span class="p">,</span>
<span class="s2">&quot;latitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_latitude</span><span class="p">,</span>
<span class="s2">&quot;library&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="n">likes</span><span class="p">,</span>
<span class="s2">&quot;live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span><span class="p">,</span>
<span class="s2">&quot;location&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span>
<span class="s2">&quot;longitude&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_longitude</span><span class="p">,</span>
<span class="s2">&quot;orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">orientation</span><span class="p">,</span>
<span class="s2">&quot;original_filename&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">,</span>
<span class="s2">&quot;original_filesize&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filesize</span><span class="p">,</span>
<span class="s2">&quot;original_height&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_height</span><span class="p">,</span>
<span class="s2">&quot;original_orientation&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_orientation</span><span class="p">,</span>
<span class="s2">&quot;original_width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_width</span><span class="p">,</span>
<span class="s2">&quot;owner&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="p">,</span>
<span class="s2">&quot;panorama&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">panorama</span><span class="p">,</span>
<span class="s2">&quot;path_derivatives&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_derivatives</span><span class="p">,</span>
<span class="s2">&quot;path_edited_live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited_live_photo</span><span class="p">,</span>
<span class="s2">&quot;path_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span><span class="p">,</span>
<span class="s2">&quot;path_live_photo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_live_photo</span><span class="p">,</span>
<span class="s2">&quot;path_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_raw</span><span class="p">,</span>
<span class="s2">&quot;path&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">,</span>
<span class="s2">&quot;person_info&quot;</span><span class="p">:</span> <span class="n">person_info</span><span class="p">,</span>
<span class="s2">&quot;persons&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">,</span>
<span class="s2">&quot;place&quot;</span><span class="p">:</span> <span class="n">place</span><span class="p">,</span>
<span class="s2">&quot;portrait&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">portrait</span><span class="p">,</span>
<span class="s2">&quot;project_info&quot;</span><span class="p">:</span> <span class="n">project_info</span><span class="p">,</span>
<span class="s2">&quot;raw_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw_original</span><span class="p">,</span>
<span class="s2">&quot;score&quot;</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span>
<span class="s2">&quot;screenshot&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">screenshot</span><span class="p">,</span>
<span class="s2">&quot;search_info_normalized&quot;</span><span class="p">:</span> <span class="n">search_info_normalized</span><span class="p">,</span>
<span class="s2">&quot;search_info&quot;</span><span class="p">:</span> <span class="n">search_info</span><span class="p">,</span>
<span class="s2">&quot;selfie&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">selfie</span><span class="p">,</span>
<span class="s2">&quot;shared&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">,</span>
<span class="s2">&quot;slow_mo&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">slow_mo</span><span class="p">,</span>
<span class="s2">&quot;time_lapse&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">time_lapse</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;tzoffset&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">tzoffset</span><span class="p">,</span>
<span class="s2">&quot;uti_edited&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_edited</span><span class="p">,</span>
<span class="s2">&quot;uti_original&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span><span class="p">,</span>
<span class="s2">&quot;uti_raw&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_raw</span><span class="p">,</span>
<span class="s2">&quot;uti&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span><span class="p">,</span>
<span class="s2">&quot;uuid&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span>
<span class="s2">&quot;visible&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">visible</span><span class="p">,</span>
<span class="s2">&quot;width&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span><span class="p">,</span>
<span class="p">}</span></div>
<div class="viewcode-block" id="PhotoInfo.json"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoInfo.json">[docs]</a> <span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">indent</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">shallow</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return JSON representation</span>
<span class="sd"> Args:</span>
<span class="sd"> indent: indent level for JSON, if None, no indent</span>
<span class="sd"> shallow: if True, return shallow JSON representation (does not contain folder_info, person_info, etc.)</span>
<span class="sd"> Returns:</span>
<span class="sd"> JSON string</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">default</span><span class="p">(</span><span class="n">o</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="k">if</span> <span class="n">shallow</span><span class="p">:</span>
<span class="c1"># delete items that are not needed for shallow JSON</span>
<span class="c1"># these are removed to match behavior of osxphotos &lt; 0.59.0 (See #999, #1039)</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;adjustments&quot;</span><span class="p">,</span>
<span class="s2">&quot;album_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_album_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_albums&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_default_pick&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_key&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_photos&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_selected&quot;</span><span class="p">,</span>
<span class="s2">&quot;cloud_metadata&quot;</span><span class="p">,</span>
<span class="s2">&quot;import_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;labels_normalized&quot;</span><span class="p">,</span>
<span class="s2">&quot;path_derivatives&quot;</span><span class="p">,</span>
<span class="s2">&quot;person_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;project_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;search_info_normalized&quot;</span><span class="p">,</span>
<span class="s2">&quot;search_info&quot;</span><span class="p">,</span>
<span class="p">]:</span>
<span class="k">del</span> <span class="n">dict_data</span><span class="p">[</span><span class="n">key</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">dict_data</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="c1"># sort lists such as keywords so JSON is consistent</span>
<span class="c1"># but do not sort certain items like location</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;location&quot;</span><span class="p">]:</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nb">dict</span><span class="p">):</span>
<span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="n">v</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span><span class="p">)</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">dict_data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="n">indent</span><span class="p">)</span></div>
<span class="k">def</span> <span class="nf">_json_hexdigest</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;JSON for use by hexdigest()&quot;&quot;&quot;</span>
<span class="c1"># This differs from json() because hexdigest must not change if metadata changed</span>
<span class="c1"># With json(), sort order of lists of dicts is not consistent but these aren&#39;t needed</span>
<span class="c1"># for computing hexdigest so we can ignore them</span>
<span class="c1"># also don&#39;t use visible because it changes based on Photos UI state</span>
<span class="k">def</span> <span class="nf">default</span><span class="p">(</span><span class="n">o</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;album_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;burst_album_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;face_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;person_info&quot;</span><span class="p">,</span>
<span class="s2">&quot;visible&quot;</span><span class="p">,</span>
<span class="p">]:</span>
<span class="k">del</span> <span class="n">dict_data</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="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">dict_data</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="c1"># sort lists such as keywords so JSON is consistent</span>
<span class="c1"># but do not sort certain items like location</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;location&quot;</span><span class="p">]:</span>
<span class="k">continue</span>
<span class="k">if</span> <span class="n">v</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nb">dict</span><span class="p">):</span>
<span class="n">dict_data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="n">v</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span><span class="p">)</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">dict_data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">default</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__eq__</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="sd">&quot;&quot;&quot;Compare two PhotoInfo objects for equality&quot;&quot;&quot;</span>
<span class="c1"># Can&#39;t just compare the two __dicts__ because some methods (like albums)</span>
<span class="c1"># memoize their value once called in an instance variable (e.g. self._albums)</span>
<span class="k">if</span> <span class="nb">isinstance</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="vm">__class__</span><span class="p">):</span>
<span class="k">return</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">db_path</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">db_path</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">uuid</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">_info</span>
<span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">def</span> <span class="fm">__ne__</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="sd">&quot;&quot;&quot;Compare two PhotoInfo objects for inequality&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__hash__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Make PhotoInfo hashable&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">hash</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span></div>
<span class="k">class</span> <span class="nc">PhotoInfoNone</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Mock class that returns None for all attributes&quot;&quot;&quot;</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="k">pass</span>
<span class="k">def</span> <span class="fm">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">frozen_photoinfo_factory</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="n">PhotoInfo</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">SimpleNamespace</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return a frozen SimpleNamespace object for a PhotoInfo object&quot;&quot;&quot;</span>
<span class="n">photo_json</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="k">def</span> <span class="nf">_object_hook</span><span class="p">(</span><span class="n">d</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="n">Any</span><span class="p">,</span> <span class="n">Any</span><span class="p">]):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">d</span><span class="p">:</span>
<span class="k">return</span> <span class="n">d</span>
<span class="c1"># if d key matches a ISO 8601 datetime (&#39;2023-03-24T06:46:57.690786&#39;, &#39;2019-07-04T16:24:01-07:00&#39;, &#39;2019-07-04T16:24:01+07:00&#39;), convert to datetime</span>
<span class="c1"># fromisoformat will also handle dates with timezone offset in form +0700, etc.</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">d</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="ow">and</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span>
<span class="sa">r</span><span class="s2">&quot;\d</span><span class="si">{4}</span><span class="s2">-\d</span><span class="si">{2}</span><span class="s2">-\d</span><span class="si">{2}</span><span class="s2">T\d</span><span class="si">{2}</span><span class="s2">:\d</span><span class="si">{2}</span><span class="s2">:\d</span><span class="si">{2}</span><span class="s2">[.]?\d*[+-]?\d</span><span class="si">{2}</span><span class="s2">[:]?\d</span><span class="si">{2}</span><span class="s2">?&quot;</span><span class="p">,</span> <span class="n">v</span>
<span class="p">):</span>
<span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</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">fromisoformat</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="k">return</span> <span class="n">SimpleNamespace</span><span class="p">(</span><span class="o">**</span><span class="n">d</span><span class="p">)</span>
<span class="n">frozen</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">photo_json</span><span class="p">,</span> <span class="n">object_hook</span><span class="o">=</span><span class="k">lambda</span> <span class="n">d</span><span class="p">:</span> <span class="n">_object_hook</span><span class="p">(</span><span class="n">d</span><span class="p">))</span>
<span class="c1"># add on json() method to frozen object</span>
<span class="k">def</span> <span class="nf">_json</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="k">return</span> <span class="n">photo_json</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">json</span> <span class="o">=</span> <span class="n">_json</span>
<span class="c1"># add hexdigest property to frozen object</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">hexdigest</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
<span class="k">def</span> <span class="nf">detected_text</span><span class="p">(</span><span class="n">confidence_threshold</span><span class="o">=</span><span class="n">TEXT_DETECTION_CONFIDENCE_THRESHOLD</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Detects text in photo and returns lists of results as (detected text, confidence)</span>
<span class="sd"> confidence_threshold: float between 0.0 and 1.0. If text detection confidence is below this threshold,</span>
<span class="sd"> text will not be returned. Default is TEXT_DETECTION_CONFIDENCE_THRESHOLD</span>
<span class="sd"> If photo is edited, uses the edited photo, otherwise the original; falls back to the preview image if neither edited or original is available</span>
<span class="sd"> Returns: list of (detected text, confidence) tuples</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">AttributeError</span><span class="p">,</span> <span class="ne">KeyError</span><span class="p">)</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="ne">AttributeError</span><span class="p">):</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text</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">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error detecting text in photo </span><span class="si">{</span><span class="n">frozen</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">&quot;</span><span class="p">)</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">confidence</span><span class="p">)</span>
<span class="k">for</span> <span class="n">text</span><span class="p">,</span> <span class="n">confidence</span> <span class="ow">in</span> <span class="n">detected_text</span>
<span class="k">if</span> <span class="n">confidence</span> <span class="o">&gt;=</span> <span class="n">confidence_threshold</span>
<span class="p">]</span>
<span class="k">return</span> <span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text_cache</span><span class="p">[</span><span class="n">confidence_threshold</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">_detected_text</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;detect text in photo, either from cached extended attribute or by attempting text detection&quot;&quot;&quot;</span>
<span class="n">path</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">path_edited</span>
<span class="k">if</span> <span class="n">frozen</span><span class="o">.</span><span class="n">hasadjustments</span> <span class="ow">and</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path_edited</span>
<span class="k">else</span> <span class="n">frozen</span><span class="o">.</span><span class="n">path</span>
<span class="p">)</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">path</span> <span class="ow">or</span> <span class="n">frozen</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="n">frozen</span><span class="o">.</span><span class="n">path_derivatives</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="n">md</span> <span class="o">=</span> <span class="n">OSXMetaData</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">decoder</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Decode value from JSON&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">val</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">))</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">md</span><span class="o">.</span><span class="n">get_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">decode</span><span class="o">=</span><span class="n">decoder</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">detected_text</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">orientation</span> <span class="o">=</span> <span class="n">frozen</span><span class="o">.</span><span class="n">orientation</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="n">detected_text</span> <span class="o">=</span> <span class="n">detect_text</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">orientation</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">encoder</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Encode value as JSON&quot;&quot;&quot;</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">return</span> <span class="n">val</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">md</span><span class="o">.</span><span class="n">set_xattr</span><span class="p">(</span>
<span class="s2">&quot;osxphotos.metadata:detected_text&quot;</span><span class="p">,</span> <span class="n">detected_text</span><span class="p">,</span> <span class="n">encode</span><span class="o">=</span><span class="n">encoder</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">detected_text</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">detected_text</span> <span class="o">=</span> <span class="n">detected_text</span>
<span class="n">frozen</span><span class="o">.</span><span class="n">_detected_text</span> <span class="o">=</span> <span class="n">_detected_text</span>
<span class="k">return</span> <span class="n">frozen</span>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 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/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>