Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9cc6ce137 | ||
|
|
bc32b1827f | ||
|
|
206ad8c33c | ||
|
|
65e8be7ed7 | ||
|
|
6477292a13 | ||
|
|
7a52d413a3 | ||
|
|
ab8b7b4b19 | ||
|
|
25d4459efd | ||
|
|
128e84c7a4 | ||
|
|
4771207595 | ||
|
|
0eca3b9c13 | ||
|
|
afec845e1b | ||
|
|
64002044d2 | ||
|
|
4e40d4b74e | ||
|
|
63d515646b | ||
|
|
0a7575b889 | ||
|
|
c776f3070d | ||
|
|
3b789242aa |
@@ -1943,7 +1943,7 @@ cog.out(get_template_field_table())
|
||||
|{lf}|A line feed: '\n', alias for {newline}|
|
||||
|{cr}|A carriage return: '\r'|
|
||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.2'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.7'|
|
||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||
|{album}|Album(s) photo is contained in|
|
||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.49.6](https://github.com/RhetTbull/osxphotos/compare/v0.49.5...v0.49.6)
|
||||
|
||||
> 22 May 2022
|
||||
|
||||
- Bug fix [`7a52d41`](https://github.com/RhetTbull/osxphotos/commit/7a52d413a3cb130a6d11413775fad88933249f36)
|
||||
- Fixed detected_text for videos [`ab8b7b4`](https://github.com/RhetTbull/osxphotos/commit/ab8b7b4b198c00569b3852b9a21b0b032fad1940)
|
||||
|
||||
#### [v0.49.5](https://github.com/RhetTbull/osxphotos/compare/v0.49.4...v0.49.5)
|
||||
|
||||
> 22 May 2022
|
||||
|
||||
- Feature inspect command [`#701`](https://github.com/RhetTbull/osxphotos/pull/701)
|
||||
- Initial implementation of inspect command [`#700`](https://github.com/RhetTbull/osxphotos/pull/700)
|
||||
- Added inspect command [`128e84c`](https://github.com/RhetTbull/osxphotos/commit/128e84c7a4b9745d70e71e3bb1c48c9952431dec)
|
||||
|
||||
#### [v0.49.4](https://github.com/RhetTbull/osxphotos/compare/v0.49.3...v0.49.4)
|
||||
|
||||
> 21 May 2022
|
||||
|
||||
- Added timestamp to export_data in exportdb, #697 [`6400204`](https://github.com/RhetTbull/osxphotos/commit/64002044d2cbbd6fb3c2f0ab69ff6cd5ee2e8e25)
|
||||
- Added warning on hardlinks to exiftool command [`4e40d4b`](https://github.com/RhetTbull/osxphotos/commit/4e40d4b74e9b244b8eee602f839e595af4f99dfb)
|
||||
|
||||
#### [v0.49.3](https://github.com/RhetTbull/osxphotos/compare/v0.49.2...v0.49.3)
|
||||
|
||||
> 21 May 2022
|
||||
|
||||
- Updated docs [`0a7575b`](https://github.com/RhetTbull/osxphotos/commit/0a7575b889949f9e74ad716bc316e87f2599d4ad)
|
||||
- Added --uuid-info, --uuid-files to exportdb [`c776f30`](https://github.com/RhetTbull/osxphotos/commit/c776f3070d40ed960eabe3c21c57c354108ff5e4)
|
||||
|
||||
#### [v0.49.2](https://github.com/RhetTbull/osxphotos/compare/v0.49.1...v0.49.2)
|
||||
|
||||
> 21 May 2022
|
||||
|
||||
- Initial implementation of exiftool command, #691 [`#696`](https://github.com/RhetTbull/osxphotos/pull/696)
|
||||
- Added exiftool command [`8e9f279`](https://github.com/RhetTbull/osxphotos/commit/8e9f27995b56489da8968e77018a8a3d1bbe76bd)
|
||||
- Added example [skip ci] [`6d5af5c`](https://github.com/RhetTbull/osxphotos/commit/6d5af5c5e87aa0699da7291376cbf05c4237f6ec)
|
||||
- Updated docs [skip ci] [`3473c2e`](https://github.com/RhetTbull/osxphotos/commit/3473c2ece2b6eea9885893c85aa9eceb16921b94)
|
||||
- Updated test [`dfcb99f`](https://github.com/RhetTbull/osxphotos/commit/dfcb99f3774cb519c30a9fcf403ca9fdc3fed993)
|
||||
|
||||
#### [v0.49.1](https://github.com/RhetTbull/osxphotos/compare/v0.49.0...v0.49.1)
|
||||
|
||||
> 17 May 2022
|
||||
|
||||
@@ -154,6 +154,7 @@ Commands:
|
||||
exportdb Utilities for working with the osxphotos export database
|
||||
help Print help; for help on commands: help <command>.
|
||||
info Print out descriptive info of the Photos library database.
|
||||
inspect Interactively inspect photos selected in Photos.
|
||||
install Install Python packages into the same environment as osxphotos
|
||||
keywords Print out keywords found in the Photos library.
|
||||
labels Print out image classification labels found in the Photos...
|
||||
@@ -1849,7 +1850,7 @@ Substitution Description
|
||||
{lf} A line feed: '\n', alias for {newline}
|
||||
{cr} A carriage return: '\r'
|
||||
{crlf} a carriage return + line feed: '\r\n'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.49.2'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.49.7'
|
||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||
|
||||
The following substitutions may result in multiple values. Thus if specified
|
||||
@@ -4025,7 +4026,7 @@ The following template field substitutions are availabe for use the templating s
|
||||
|{lf}|A line feed: '\n', alias for {newline}|
|
||||
|{cr}|A carriage return: '\r'|
|
||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.2'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.7'|
|
||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||
|{album}|Album(s) photo is contained in|
|
||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||
|
||||
@@ -107,6 +107,7 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
|
||||
exportdb Utilities for working with the osxphotos export database
|
||||
help Print help; for help on commands: help <command>.
|
||||
info Print out descriptive info of the Photos library database.
|
||||
inspect Interactively inspect photos selected in Photos.
|
||||
install Install Python packages into the same environment as osxphotos
|
||||
keywords Print out keywords found in the Photos library.
|
||||
labels Print out image classification labels found in the Photos...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 3a7efae54b6e07cdaf59f7d2e9729b6e
|
||||
config: 0da8263b5cc3d544b725c487a729f7a1
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>Overview: module code - osxphotos 0.49.2 documentation</title>
|
||||
<title>Overview: module code - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.export_db - osxphotos 0.49.2 documentation</title>
|
||||
<title>osxphotos.export_db - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -202,8 +202,10 @@
|
||||
<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">pickle</span>
|
||||
<span class="kn">import</span> <span class="nn">re</span>
|
||||
<span class="kn">import</span> <span class="nn">sqlite3</span>
|
||||
<span class="kn">import</span> <span class="nn">sys</span>
|
||||
<span class="kn">import</span> <span class="nn">time</span>
|
||||
@@ -211,7 +213,7 @@
|
||||
<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">StringIO</span>
|
||||
<span class="kn">from</span> <span class="nn">sqlite3</span> <span class="kn">import</span> <span class="n">Error</span>
|
||||
<span class="kn">from</span> <span class="nn">tempfile</span> <span class="kn">import</span> <span class="n">TemporaryDirectory</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">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</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">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">tenacity</span> <span class="kn">import</span> <span class="n">retry</span><span class="p">,</span> <span class="n">stop_after_attempt</span>
|
||||
|
||||
@@ -226,7 +228,7 @@
|
||||
<span class="s2">"ExportDBTemp"</span><span class="p">,</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="n">OSXPHOTOS_EXPORTDB_VERSION</span> <span class="o">=</span> <span class="s2">"7.0"</span>
|
||||
<span class="n">OSXPHOTOS_EXPORTDB_VERSION</span> <span class="o">=</span> <span class="s2">"7.1"</span>
|
||||
<span class="n">OSXPHOTOS_ABOUT_STRING</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"Created by osxphotos version </span><span class="si">{</span><span class="n">__version__</span><span class="si">}</span><span class="s2"> (https://github.com/RhetTbull/osxphotos) on </span><span class="si">{</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="si">}</span><span class="s2">"</span>
|
||||
|
||||
<span class="c1"># max retry attempts for methods which use tenacity.retry</span>
|
||||
@@ -367,6 +369,17 @@
|
||||
<span class="n">uuid</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">return</span> <span class="n">uuid</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_files_for_uuid"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_files_for_uuid">[docs]</a> <span class="k">def</span> <span class="nf">get_files_for_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">:</span>
|
||||
<span class="sd">"""query database for UUID and return list of files associated with UUID or empty list"""</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="s2">"SELECT filepath FROM export_data WHERE uuid = ?"</span><span class="p">,</span>
|
||||
<span class="p">(</span><span class="n">uuid</span><span class="p">,),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">fetchall</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">export_dir</span><span class="p">,</span> <span class="n">r</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">]</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_photoinfo_for_uuid"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid">[docs]</a> <span class="k">def</span> <span class="nf">get_photoinfo_for_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">):</span>
|
||||
<span class="sd">"""returns the photoinfo JSON struct for a UUID"""</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
@@ -394,6 +407,35 @@
|
||||
<span class="k">except</span> <span class="n">Error</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="n">e</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_target_for_file"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_target_for_file">[docs]</a> <span class="k">def</span> <span class="nf">get_target_for_file</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">filename</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</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="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
|
||||
<span class="sd">"""query database for file matching file name and return the matching filename if there is one;</span>
|
||||
<span class="sd"> otherwise return None; looks for file.ext, file (1).ext, file (2).ext and so on to find the</span>
|
||||
<span class="sd"> actual target name that was used to export filename</span>
|
||||
|
||||
<span class="sd"> Returns: the matching filename or None if no match found</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">filepath_normalized</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_normalize_filepath_relative</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||||
<span class="n">filepath_stem</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">splitext</span><span class="p">(</span><span class="n">filepath_normalized</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="s2">"SELECT uuid, filepath, filepath_normalized FROM export_data WHERE uuid = ? AND filepath_normalized LIKE ?"</span><span class="p">,</span>
|
||||
<span class="p">(</span>
|
||||
<span class="n">uuid</span><span class="p">,</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">filepath_stem</span><span class="si">}</span><span class="s2">%"</span><span class="p">,</span>
|
||||
<span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
|
||||
<span class="n">filepath_normalized</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">splitext</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="mi">2</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">filepath_stem</span><span class="p">)</span> <span class="o">+</span> <span class="sa">r</span><span class="s2">"(\s\(\d+\))?$"</span><span class="p">,</span> <span class="n">filepath_normalized</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">export_dir</span><span class="p">,</span> <span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||||
|
||||
<span class="k">return</span> <span class="kc">None</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_previous_uuids"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_previous_uuids">[docs]</a> <span class="k">def</span> <span class="nf">get_previous_uuids</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""returns list of UUIDs of previously exported photos found in export database"""</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
@@ -666,6 +708,10 @@
|
||||
<span class="c1"># create report_data table</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_migrate_6_0_to_7_0</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">version</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="s2">"7.1"</span><span class="p">:</span>
|
||||
<span class="c1"># add timestamp to export_data</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_migrate_7_0_to_7_1</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"VACUUM;"</span><span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
|
||||
@@ -883,6 +929,32 @@
|
||||
<span class="k">except</span> <span class="n">Error</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="n">e</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_migrate_7_0_to_7_1</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">):</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;"""</span><span class="p">)</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> CREATE TRIGGER insert_timestamp_trigger</span>
|
||||
<span class="sd"> AFTER INSERT ON export_data</span>
|
||||
<span class="sd"> BEGIN</span>
|
||||
<span class="sd"> UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;</span>
|
||||
<span class="sd"> END;</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> CREATE TRIGGER update_timestamp_trigger</span>
|
||||
<span class="sd"> AFTER UPDATE On export_data</span>
|
||||
<span class="sd"> BEGIN</span>
|
||||
<span class="sd"> UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;</span>
|
||||
<span class="sd"> END;</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
<span class="k">except</span> <span class="n">Error</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="n">e</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_perform_db_maintenace</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">):</span>
|
||||
<span class="sd">"""Perform database maintenance"""</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
@@ -1244,6 +1316,21 @@
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_manager</span><span class="p">:</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">timestamp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""returns the timestamp value"""</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">row</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="s2">"SELECT timestamp FROM export_data WHERE filepath_normalized = ?;"</span><span class="p">,</span>
|
||||
<span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath_normalized</span><span class="p">,),</span>
|
||||
<span class="p">)</span><span class="o">.</span><span class="n">fetchone</span><span class="p">():</span>
|
||||
<span class="k">return</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</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">"No timestamp found in database for </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath_normalized</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return dict of self"""</span>
|
||||
<span class="n">exifdata</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exifdata</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">exifdata</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
@@ -1252,6 +1339,7 @@
|
||||
<span class="s2">"filepath"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filepath</span><span class="p">,</span>
|
||||
<span class="s2">"filepath_normalized"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">filepath_normalized</span><span class="p">,</span>
|
||||
<span class="s2">"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">"timestamp"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">timestamp</span><span class="p">,</span>
|
||||
<span class="s2">"digest"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">digest</span><span class="p">,</span>
|
||||
<span class="s2">"src_sig"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">src_sig</span><span class="p">,</span>
|
||||
<span class="s2">"dest_sig"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">dest_sig</span><span class="p">,</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.49.2 documentation</title>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -240,7 +240,13 @@
|
||||
<span class="kn">from</span> <span class="nn">.phototemplate</span> <span class="kn">import</span> <span class="n">RenderOptions</span>
|
||||
<span class="kn">from</span> <span class="nn">.rich_utils</span> <span class="kn">import</span> <span class="n">add_rich_markup_tag</span>
|
||||
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">hexdigest</span><span class="p">,</span> <span class="n">increment_filename</span><span class="p">,</span> <span class="n">lineno</span><span class="p">,</span> <span class="n">list_directory</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="p">(</span>
|
||||
<span class="n">hexdigest</span><span class="p">,</span>
|
||||
<span class="n">increment_filename</span><span class="p">,</span>
|
||||
<span class="n">increment_filename_with_count</span><span class="p">,</span>
|
||||
<span class="n">lineno</span><span class="p">,</span>
|
||||
<span class="n">list_directory</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="s2">"ExportError"</span><span class="p">,</span>
|
||||
@@ -684,7 +690,7 @@
|
||||
<span class="n">src</span> <span class="o">=</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">edited</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">edited</span> <span class="k">else</span> <span class="n">staged_files</span><span class="o">.</span><span class="n">original</span>
|
||||
|
||||
<span class="c1"># get the right destination path depending on options.update, etc.</span>
|
||||
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_dest_path</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">dest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_dest_path</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_render_options</span><span class="o">.</span><span class="n">filepath</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="n">all_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
||||
@@ -838,12 +844,11 @@
|
||||
<span class="k">return</span> <span class="n">edited_filename</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_get_dest_path</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span> <span class="n">src</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
||||
<span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span>
|
||||
<span class="p">)</span> <span class="o">-></span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">:</span>
|
||||
<span class="sd">"""If destination exists find match in ExportDB, on disk, or add (1), (2), and so on to filename to get a valid destination</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> src (str): source file path</span>
|
||||
<span class="sd"> dest (str): destination path</span>
|
||||
<span class="sd"> options (ExportOptions): Export options</span>
|
||||
|
||||
@@ -859,6 +864,10 @@
|
||||
<span class="sa">f</span><span class="s2">"destination exists (</span><span class="si">{</span><span class="n">dest</span><span class="si">}</span><span class="s2">); overwrite=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="si">}</span><span class="s2">, increment=</span><span class="si">{</span><span class="n">options</span><span class="o">.</span><span class="n">increment</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># if overwrite, we don't care if the file exists or not</span>
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">dest</span>
|
||||
|
||||
<span class="c1"># if not update or overwrite, check to see if file exists and if so, add (1), (2), etc</span>
|
||||
<span class="c1"># until we find one that works</span>
|
||||
<span class="c1"># Photos checks the stem and adds (1), (2), etc which avoids collision with sidecars</span>
|
||||
@@ -871,29 +880,36 @@
|
||||
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">))</span>
|
||||
|
||||
<span class="c1"># if update and file exists, need to check to see if it's the right file by checking export db</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">)</span> <span class="ow">and</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="n">src</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span> <span class="ow">or</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">:</span>
|
||||
<span class="n">export_db</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">export_db</span>
|
||||
<span class="c1"># destination exists, check to see if destination is the right UUID</span>
|
||||
<span class="n">dest_uuid</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
|
||||
<span class="c1"># not the right file, find the right one</span>
|
||||
<span class="c1"># find files that match "dest_name (*.ext" (e.g. "dest_name (1).jpg", "dest_name (2).jpg)", ...)</span>
|
||||
<span class="n">dest_files</span> <span class="o">=</span> <span class="n">list_directory</span><span class="p">(</span>
|
||||
<span class="n">dest</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span>
|
||||
<span class="n">startswith</span><span class="o">=</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2"> ("</span><span class="p">,</span>
|
||||
<span class="n">endswith</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">suffix</span><span class="p">,</span>
|
||||
<span class="n">include_path</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">for</span> <span class="n">file_</span> <span class="ow">in</span> <span class="n">dest_files</span><span class="p">:</span>
|
||||
<span class="n">dest_uuid</span> <span class="o">=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">file_</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
|
||||
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">file_</span><span class="p">)</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># increment the destination file</span>
|
||||
<span class="n">dest</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">increment_filename</span><span class="p">(</span><span class="n">dest</span><span class="p">))</span>
|
||||
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">dest</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
||||
<span class="c1"># destination doesn't exist in export db and doesn't exist on disk</span>
|
||||
<span class="c1"># so we can just use it</span>
|
||||
<span class="k">return</span> <span class="n">dest</span>
|
||||
|
||||
<span class="c1"># either dest was updated in the if clause above or not updated at all</span>
|
||||
<span class="k">if</span> <span class="n">dest_uuid</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">:</span>
|
||||
<span class="c1"># destination is the right file</span>
|
||||
<span class="k">return</span> <span class="n">dest</span>
|
||||
|
||||
<span class="c1"># either dest_uuid is wrong or file exists and there's no associated UUID, so find a name that matches</span>
|
||||
<span class="c1"># or create a new name if no match</span>
|
||||
<span class="c1"># find files that match "dest_name (*.ext" (e.g. "dest_name (1).jpg", "dest_name (2).jpg)", ...)</span>
|
||||
<span class="c1"># first, find all matching files in export db and see if there's a match</span>
|
||||
<span class="k">if</span> <span class="n">dest_target</span> <span class="o">:=</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_target_for_file</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">,</span> <span class="n">dest</span><span class="p">):</span>
|
||||
<span class="c1"># there's a match so use that</span>
|
||||
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest_target</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># no match so need to create a new name</span>
|
||||
<span class="c1"># increment the destination file until we find one that doesn't exist and doesn't match another uuid in the database</span>
|
||||
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
|
||||
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
<span class="k">while</span> <span class="n">export_db</span><span class="o">.</span><span class="n">get_uuid_for_file</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">dest</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="n">increment_filename_with_count</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># fail safe...I can't think of a case that gets here</span>
|
||||
<span class="k">return</span> <span class="n">dest</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_should_update_photo</span><span class="p">(</span>
|
||||
@@ -1232,9 +1248,9 @@
|
||||
|
||||
<span class="k">def</span> <span class="nf">_export_photo</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">src</span><span class="p">,</span>
|
||||
<span class="n">dest</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">,</span>
|
||||
<span class="n">src</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||||
<span class="n">dest</span><span class="p">:</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""Helper function for export()</span>
|
||||
<span class="sd"> Does the actual copy or hardlink taking the appropriate</span>
|
||||
@@ -1640,7 +1656,7 @@
|
||||
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
||||
<span class="sd">"""Write exif metadata to src file using exiftool</span>
|
||||
|
||||
<span class="sd"> Caution: This method modifies *src*, not *dest*, </span>
|
||||
<span class="sd"> Caution: This method modifies *src*, not *dest*,</span>
|
||||
<span class="sd"> so src must be a copy of the original file if you don't want the source modified;</span>
|
||||
<span class="sd"> it also does not write to dest (dest is the intended destination for purposes of</span>
|
||||
<span class="sd"> referencing the export database. This allows the exiftool update to be done on the</span>
|
||||
|
||||
@@ -320,7 +320,7 @@ Template Substitutions
|
||||
* - {crlf}
|
||||
- a carriage return + line feed: '\r\n'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.49.2'
|
||||
- The osxphotos version, e.g. '0.49.7'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.49.2',
|
||||
VERSION: '0.49.7',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -1278,6 +1278,16 @@ to modify this behavior.</p>
|
||||
<dd><p>Print information about FILE_PATH contained in the database.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-uuid-files">
|
||||
<span class="sig-name descname"><span class="pre">--uuid-files</span></span><span class="sig-prename descclassname"> <span class="pre"><UUID></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-uuid-files" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>List exported files associated with UUID.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-uuid-info">
|
||||
<span class="sig-name descname"><span class="pre">--uuid-info</span></span><span class="sig-prename descclassname"> <span class="pre"><UUID></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-uuid-info" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print information about UUID contained in the database.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-report">
|
||||
<span class="sig-name descname"><span class="pre">--report</span></span><span class="sig-prename descclassname"> <span class="pre"><REPORT_FILE</span> <span class="pre">RUN_ID></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-report" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Generate an export report as <cite>osxphotos export … –report REPORT_FILE</cite> would have done. This allows you to re-create an export report if you didn’t use the –report option when running <cite>osxphotos export</cite>. The extension of the report file is used to determine the format. Valid extensions are: .csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). RUN_ID may be any integer from -10 to 0 specifying which run to use. For example, <cite>–report report.csv 0</cite> will generate a CSV report for the last run and <cite>–report report.json -1</cite> will generate a JSON report for the second-to-last run (one run prior to last run). REPORT_FILE may be a template string (see Templating System), for example, –report ‘export_{today.date}.csv’ will write a CSV report file named with today’s date. See also –append.</p>
|
||||
@@ -1361,6 +1371,38 @@ to modify this behavior.</p>
|
||||
<dd><p>Optional argument(s)</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-inspect">
|
||||
<h3>inspect<a class="headerlink" href="#osxphotos-inspect" title="Permalink to this headline">#</a></h3>
|
||||
<p>Interactively inspect photos selected in Photos.</p>
|
||||
<p>Open Photos then run <cite>osxphotos inspect</cite> in the terminal.
|
||||
As you select a photo in Photos, inspect will display metadata about the photo.
|
||||
Press Ctrl+C to exit when done.
|
||||
Works best with a modern terminal like iTerm2 or Kitty.</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos inspect <span class="o">[</span>OPTIONS<span class="o">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p class="rubric">Options</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-inspect-t">
|
||||
<span id="cmdoption-osxphotos-inspect-detect-text"></span><span class="sig-name descname"><span class="pre">-t</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--detect-text</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-inspect-t" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Detect text in photos</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-inspect-theme">
|
||||
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre"><THEME></span></span><a class="headerlink" href="#cmdoption-osxphotos-inspect-theme" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Specify the color theme to use for –verbose output. Valid themes are ‘dark’, ‘light’, ‘mono’, and ‘plain’. Defaults to ‘dark’ or ‘light’ depending on system dark mode setting.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Options</dt>
|
||||
<dd class="field-odd"><p>dark | light | mono | plain</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-inspect-db">
|
||||
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre"><PHOTOS_LIBRARY_PATH></span></span><a class="headerlink" href="#cmdoption-osxphotos-inspect-db" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either –db or directly as PHOTOS_LIBRARY positional argument. If neither –db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-install">
|
||||
<h3>install<a class="headerlink" href="#osxphotos-install" title="Permalink to this headline">#</a></h3>
|
||||
<p>Install Python packages into the same environment as osxphotos</p>
|
||||
@@ -2705,6 +2747,7 @@ Commands:
|
||||
<li><a class="reference internal" href="#osxphotos-exportdb">exportdb</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-help">help</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-info">info</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-inspect">inspect</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-install">install</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-keywords">keywords</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-labels">labels</a></li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.49.2 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -122,7 +122,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -145,7 +145,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -402,6 +402,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-db">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-info-db">osxphotos-info command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-db">osxphotos-inspect command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-keywords-db">osxphotos-keywords command line option</a>
|
||||
</li>
|
||||
@@ -483,6 +485,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-description-template">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-description-template">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--detect-text
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">osxphotos-inspect command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1143,8 +1152,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-title">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--not-burst
|
||||
|
||||
@@ -1156,6 +1163,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-burst">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--not-cloudasset
|
||||
|
||||
@@ -1714,6 +1723,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-theme">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-theme">osxphotos-inspect command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-theme">osxphotos-timewarp command line option</a>
|
||||
</li>
|
||||
@@ -1872,6 +1883,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-uuid">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-uuid">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--uuid-files
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-uuid-files">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1883,6 +1901,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-uuid-from-file">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-uuid-from-file">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--uuid-info
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-uuid-info">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -2072,6 +2097,8 @@
|
||||
-t
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">osxphotos-inspect command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-t">osxphotos-timewarp command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -2464,6 +2491,8 @@
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_exported_files">get_exported_files() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_file_record">get_file_record() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_files_for_uuid">get_files_for_uuid() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_media_type">get_media_type() (osxphotos.PhotoTemplate method)</a>
|
||||
</li>
|
||||
@@ -2476,6 +2505,8 @@
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_previous_uuids">get_previous_uuids() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_target_for_file">get_target_for_file() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_template_value">get_template_value() (osxphotos.PhotoTemplate method)</a>
|
||||
</li>
|
||||
@@ -3248,6 +3279,10 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-touch-file">--touch-file</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-update-signatures">--update-signatures</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-uuid-files">--uuid-files</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-uuid-info">--uuid-info</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-vacuum">--vacuum</a>
|
||||
</li>
|
||||
@@ -3278,6 +3313,19 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-info-json">--json</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-info-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
osxphotos-inspect command line option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-db">--db</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">--detect-text</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-theme">--theme</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-inspect-t">-t</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -3313,6 +3361,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
osxphotos-list command line option
|
||||
|
||||
@@ -3320,8 +3370,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-list-json">--json</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
osxphotos-persons command line option
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos 0.49.2 documentation</title>
|
||||
<title>osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="#"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="#"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -242,6 +242,7 @@
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-exportdb">exportdb</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-help">help</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-info">info</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-inspect">inspect</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-install">install</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-keywords">keywords</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-labels">labels</a></li>
|
||||
|
||||
BIN
docs/objects.inv
BIN
docs/objects.inv
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotos’s documentation!" href="index.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos python API" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.49.2 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -122,7 +122,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -145,7 +145,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos python API - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos python API - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -423,6 +423,11 @@ If called in context manager, returns True (execution is delayed until exiting c
|
||||
<p>Returns: an ExportRecord object or None if filename not found</p>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_files_for_uuid">
|
||||
<span class="sig-name descname"><span class="pre">get_files_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">List</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_files_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_files_for_uuid" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>query database for UUID and return list of files associated with UUID or empty list</p>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_photoinfo_for_uuid">
|
||||
<span class="sig-name descname"><span class="pre">get_photoinfo_for_uuid</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_photoinfo_for_uuid"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_photoinfo_for_uuid" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>returns the photoinfo JSON struct for a UUID</p>
|
||||
@@ -433,6 +438,16 @@ If called in context manager, returns True (execution is delayed until exiting c
|
||||
<dd><p>returns list of UUIDs of previously exported photos found in export database</p>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_target_for_file">
|
||||
<span class="sig-name descname"><span class="pre">get_target_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">uuid</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">pathlib.Path</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_target_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_target_for_file" title="Permalink to this definition">#</a></dt>
|
||||
<dd><dl class="simple">
|
||||
<dt>query database for file matching file name and return the matching filename if there is one;</dt><dd><p>otherwise return None; looks for file.ext, file (1).ext, file (2).ext and so on to find the
|
||||
actual target name that was used to export filename</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>Returns: the matching filename or None if no match found</p>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_uuid_for_file">
|
||||
<span class="sig-name descname"><span class="pre">get_uuid_for_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_uuid_for_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_uuid_for_file" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>query database for filename and return UUID
|
||||
|
||||
BIN
docs/screencast/osxphotos-inspect.gif
Normal file
BIN
docs/screencast/osxphotos-inspect.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 MiB |
@@ -4,7 +4,7 @@
|
||||
<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="#" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.49.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.49.7 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||
@@ -121,7 +121,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -144,7 +144,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Template System - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos Template System - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -567,7 +567,7 @@
|
||||
<td><p>a carriage return + line feed: ‘rn’</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.49.2’</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.49.7’</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
|
||||
<td><p>The full command line used to run osxphotos</p></td>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.49.2 documentation</title>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.49.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -320,7 +320,7 @@ Template Substitutions
|
||||
* - {crlf}
|
||||
- a carriage return + line feed: '\r\n'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.49.2'
|
||||
- The osxphotos version, e.g. '0.49.7'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.49.2"
|
||||
__version__ = "0.49.7"
|
||||
|
||||
@@ -59,6 +59,7 @@ from .keywords import keywords
|
||||
from .labels import labels
|
||||
from .list import _list_libraries, list_libraries
|
||||
from .persons import persons
|
||||
from .photo_inspect import photo_inspect
|
||||
from .places import places
|
||||
from .query import query
|
||||
from .repl import repl
|
||||
@@ -88,6 +89,7 @@ __all__ = [
|
||||
"list_libraries",
|
||||
"load_uuid_from_file",
|
||||
"persons",
|
||||
"photo_inspect",
|
||||
"places",
|
||||
"query",
|
||||
"repl",
|
||||
|
||||
@@ -21,6 +21,7 @@ from .keywords import keywords
|
||||
from .labels import labels
|
||||
from .list import list_libraries
|
||||
from .persons import persons
|
||||
from .photo_inspect import photo_inspect
|
||||
from .places import places
|
||||
from .query import query
|
||||
from .repl import repl
|
||||
@@ -79,6 +80,7 @@ for command in [
|
||||
labels,
|
||||
list_libraries,
|
||||
persons,
|
||||
photo_inspect,
|
||||
places,
|
||||
query,
|
||||
repl,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""exiftool command for osxphotos CLI to update an previous export with exiftool metadata"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Callable
|
||||
@@ -18,7 +19,6 @@ from osxphotos.utils import pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
rich_click_echo,
|
||||
rich_echo,
|
||||
rich_echo_error,
|
||||
set_rich_console,
|
||||
set_rich_theme,
|
||||
@@ -343,6 +343,8 @@ def process_files(
|
||||
total = len(files)
|
||||
count = 1
|
||||
all_results = ExportResults()
|
||||
# process files that are hardlinked? Requires user confirmation
|
||||
hardlink_ok = False
|
||||
with rich_progress(console=get_verbose_console(), mock=options.verbose) as progress:
|
||||
task = progress.add_task("Processing files", total=total)
|
||||
for uuid, file in files:
|
||||
@@ -350,7 +352,14 @@ def process_files(
|
||||
verbose(f"Skipping missing file [filepath]{file}[/]")
|
||||
report_writer.write(ExportResults(missing=[file]))
|
||||
continue
|
||||
# zzz put in check for hardlink
|
||||
if not hardlink_ok and os.stat(file).st_nlink > 1:
|
||||
rich_click_echo(
|
||||
f"[warning]:warning-emoji: Warning: file [filepath]{file}[/] is hardlinked.\n"
|
||||
"You may be modifying linked original files in your Photos library. "
|
||||
"This is inadvisable.[/]",
|
||||
)
|
||||
click.confirm("Continue processing hardlinks?", abort=True)
|
||||
hardlink_ok = True
|
||||
verbose(f"Processing file [filepath]{file}[/] ([num]{count}/{total}[/num])")
|
||||
photo = photosdb.get_photo(uuid)
|
||||
export_options = ExportOptions(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""exportdb command for osxphotos CLI"""
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
@@ -63,6 +64,18 @@ from .verbose import verbose_print
|
||||
nargs=1,
|
||||
help="Print information about FILE_PATH contained in the database.",
|
||||
)
|
||||
@click.option(
|
||||
"--uuid-files",
|
||||
metavar="UUID",
|
||||
nargs=1,
|
||||
help="List exported files associated with UUID.",
|
||||
)
|
||||
@click.option(
|
||||
"--uuid-info",
|
||||
metavar="UUID",
|
||||
nargs=1,
|
||||
help="Print information about UUID contained in the database.",
|
||||
)
|
||||
@click.option(
|
||||
"--report",
|
||||
metavar="REPORT_FILE RUN_ID",
|
||||
@@ -110,22 +123,24 @@ from .verbose import verbose_print
|
||||
)
|
||||
@click.argument("export_db", metavar="EXPORT_DATABASE", type=click.Path(exists=True))
|
||||
def exportdb(
|
||||
version,
|
||||
vacuum,
|
||||
check_signatures,
|
||||
update_signatures,
|
||||
touch_file,
|
||||
last_run,
|
||||
save_config,
|
||||
info,
|
||||
report,
|
||||
migrate,
|
||||
sql,
|
||||
export_dir,
|
||||
append,
|
||||
verbose,
|
||||
check_signatures,
|
||||
dry_run,
|
||||
export_db,
|
||||
export_dir,
|
||||
info,
|
||||
last_run,
|
||||
migrate,
|
||||
report,
|
||||
save_config,
|
||||
sql,
|
||||
touch_file,
|
||||
update_signatures,
|
||||
uuid_files,
|
||||
uuid_info,
|
||||
vacuum,
|
||||
verbose,
|
||||
version,
|
||||
):
|
||||
"""Utilities for working with the osxphotos export database"""
|
||||
|
||||
@@ -163,6 +178,8 @@ def exportdb(
|
||||
sql,
|
||||
touch_file,
|
||||
update_signatures,
|
||||
uuid_files,
|
||||
uuid_info,
|
||||
vacuum,
|
||||
version,
|
||||
]
|
||||
@@ -273,6 +290,37 @@ def exportdb(
|
||||
print(f"[red]File '{info}' not found in export database[/red]")
|
||||
sys.exit(0)
|
||||
|
||||
if uuid_info:
|
||||
# get photoinfo record for a uuid
|
||||
exportdb = ExportDB(export_db, export_dir)
|
||||
try:
|
||||
info_rec = exportdb.get_photoinfo_for_uuid(uuid_info)
|
||||
except Exception as e:
|
||||
print(f"[red]Error: {e}[/red]")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if info_rec:
|
||||
print(json.dumps(json.loads(info_rec), sort_keys=True, indent=2))
|
||||
else:
|
||||
print(f"[red]UUID '{uuid_info}' not found in export database[/red]")
|
||||
sys.exit(0)
|
||||
|
||||
if uuid_files:
|
||||
# list files associated with a uuid
|
||||
exportdb = ExportDB(export_db, export_dir)
|
||||
try:
|
||||
file_list = exportdb.get_files_for_uuid(uuid_files)
|
||||
except Exception as e:
|
||||
print(f"[red]Error: {e}[/red]")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if file_list:
|
||||
for f in file_list:
|
||||
print(f)
|
||||
else:
|
||||
print(f"[red]UUID '{uuid_files}' not found in export database[/red]")
|
||||
sys.exit(0)
|
||||
|
||||
if report:
|
||||
exportdb = ExportDB(export_db, export_dir)
|
||||
report_template, run_id = report
|
||||
|
||||
475
osxphotos/cli/photo_inspect.py
Normal file
475
osxphotos/cli/photo_inspect.py
Normal file
@@ -0,0 +1,475 @@
|
||||
"""Inspect photos selected in Photos """
|
||||
|
||||
import functools
|
||||
import re
|
||||
from fractions import Fraction
|
||||
from multiprocessing import Process, Queue
|
||||
from queue import Empty
|
||||
from time import gmtime, sleep, strftime
|
||||
from typing import List, Optional, Tuple
|
||||
import pathlib
|
||||
|
||||
import bitmath
|
||||
import click
|
||||
from applescript import ScriptError
|
||||
from photoscript import PhotosLibrary
|
||||
from rich.console import Console
|
||||
from rich.layout import Layout
|
||||
from rich.live import Live
|
||||
from rich.panel import Panel
|
||||
|
||||
from osxphotos import PhotoInfo, PhotosDB
|
||||
from osxphotos._constants import _UNKNOWN_PERSON
|
||||
from osxphotos.rich_utils import add_rich_markup_tag
|
||||
from osxphotos.text_detection import detect_text as detect_text_in_photo
|
||||
from osxphotos.utils import dd_to_dms_str
|
||||
|
||||
from .color_themes import get_theme
|
||||
from .common import DB_OPTION, THEME_OPTION, get_photos_db
|
||||
|
||||
# global that tracks UUID being inspected
|
||||
CURRENT_UUID = None
|
||||
|
||||
# helpers for markup
|
||||
bold = add_rich_markup_tag("bold")
|
||||
dim = add_rich_markup_tag("dim")
|
||||
|
||||
|
||||
def extract_uuid(text: str) -> str:
|
||||
"""Extract a UUID from a string"""
|
||||
if match := re.search(
|
||||
r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",
|
||||
text,
|
||||
):
|
||||
return match[1]
|
||||
return None
|
||||
|
||||
|
||||
def trim(text: str, pad: str = "") -> str:
|
||||
"""Truncate a string to a fit in console, - len(pad) - 4; also removes new lines"""
|
||||
width = Console().width - len(pad) - 4
|
||||
text = text.replace("\n", " ")
|
||||
return text if len(text) <= width else f"{text[: width- 3]}..."
|
||||
|
||||
|
||||
def inspect_photo(photo: PhotoInfo, detected_text: Optional[str] = None) -> str:
|
||||
"""Get info about an osxphotos PhotoInfo object formatted for printing"""
|
||||
|
||||
properties = [
|
||||
bold("Filename: ") + f"[filename]{photo.original_filename}[/]",
|
||||
bold("Type: ") + get_photo_type(photo),
|
||||
bold("UUID: ") + f"[uuid]{photo.uuid}[/]",
|
||||
bold("Date: ") + f"[time]{photo.date.isoformat()}[/]",
|
||||
bold("Date added: ") + f"[time]{photo.date_added.isoformat()}[/]",
|
||||
]
|
||||
if photo.date_modified:
|
||||
properties.append(
|
||||
bold("Date modified: ") + f"[time]{photo.date_modified.isoformat()}[/]"
|
||||
)
|
||||
|
||||
if photo.intrash and photo.date_trashed:
|
||||
properties.append(
|
||||
bold("Date deleted: ") + f"[time]{photo.date_trashed.isoformat()}[/]"
|
||||
)
|
||||
|
||||
if photo.import_info and photo.import_info.creation_date:
|
||||
properties.append(
|
||||
bold("Date imported: ")
|
||||
+ f"[time]{photo.import_info.creation_date.isoformat()}[/]"
|
||||
)
|
||||
|
||||
properties.extend(
|
||||
[
|
||||
bold("Dimensions: ")
|
||||
+ f"[num]{photo.width}[/] x [num]{photo.height}[/] "
|
||||
+ bold("Orientation: ")
|
||||
+ f"[num]{photo.orientation}[/]",
|
||||
bold("File size: ")
|
||||
+ f"[num]{float(bitmath.Byte(photo.original_filesize).to_MB()):.2f} MB[/]",
|
||||
bold("Title: ") + f"{photo.title or '-'}",
|
||||
bold("Description: ")
|
||||
+ f"{trim(photo.description or '-', 'Description: ')}",
|
||||
bold("Edited: ")
|
||||
+ f"{'✔' if photo.hasadjustments else '-'} "
|
||||
+ bold("External edits: ")
|
||||
+ f"{'✔' if photo.external_edit else '-'}",
|
||||
bold("Keywords: ") + f"{', '.join(photo.keywords) or '-'}",
|
||||
bold("Persons: ")
|
||||
+ f"{', '.join(p for p in photo.persons if p != _UNKNOWN_PERSON) or '-'}",
|
||||
bold("Location: ")
|
||||
+ f"{', '.join(dd_to_dms_str(*photo.location)) if photo.location[0] else '-'}",
|
||||
bold("Place: ") + f"{photo.place.name if photo.place else '-'}",
|
||||
bold("Categories: ") + f"{', '.join(photo.labels) or '-'}",
|
||||
]
|
||||
)
|
||||
properties.append(format_flags(photo))
|
||||
properties.append(format_albums(photo))
|
||||
|
||||
if photo.project_info:
|
||||
properties.append(
|
||||
bold("Projects: ")
|
||||
+ f"{', '.join(p.title for p in photo.project_info) or '-'}"
|
||||
)
|
||||
|
||||
if photo.moment_info:
|
||||
properties.append(bold("Moment: ") + f"{photo.moment_info.title or '-'}")
|
||||
|
||||
if photo.comments:
|
||||
comments = [f"{c.user}: {c.text}" for c in photo.comments]
|
||||
properties.append(
|
||||
bold("Comments: ") + trim(f"{', '.join(comments)}", "Comments: ")
|
||||
)
|
||||
|
||||
if photo.likes:
|
||||
properties.append(
|
||||
bold("Likes: ")
|
||||
+ trim(f"{', '.join(l.user for l in photo.likes)}", "Likes: ")
|
||||
)
|
||||
|
||||
properties.append(format_exif_info(photo))
|
||||
properties.append(format_score_info(photo))
|
||||
|
||||
if detected_text:
|
||||
# have detected text for this photo
|
||||
properties.append(
|
||||
bold("Detected text: ") + trim(detected_text, "Detected text: ")
|
||||
)
|
||||
|
||||
properties.append(format_paths(photo))
|
||||
|
||||
return "\n".join(properties)
|
||||
|
||||
|
||||
def format_score_info(photo: PhotoInfo) -> str:
|
||||
"""Format score_info"""
|
||||
score_str = bold("Score: ")
|
||||
if photo.score:
|
||||
score_str += f"[num]{photo.score.overall}[/]" if photo.score else "-"
|
||||
return score_str
|
||||
|
||||
|
||||
def format_flags(photo: PhotoInfo) -> str:
|
||||
"""Format special properties"""
|
||||
flag_str = bold("Flags: ")
|
||||
flags = []
|
||||
if photo.favorite:
|
||||
flags.append("favorite")
|
||||
if photo.visible:
|
||||
flags.append("visible")
|
||||
if photo.hidden:
|
||||
flags.append("hidden")
|
||||
if photo.ismissing:
|
||||
flags.append("missing")
|
||||
if photo.intrash:
|
||||
flags.append("in trash")
|
||||
if photo.iscloudasset:
|
||||
flags.append("cloud asset")
|
||||
if photo.incloud:
|
||||
flags.append("in cloud")
|
||||
if photo.shared:
|
||||
flags.append("shared")
|
||||
|
||||
flag_str += f"{', '.join(flags) or '-'}"
|
||||
return flag_str
|
||||
|
||||
|
||||
def format_albums(photo: PhotoInfo) -> str:
|
||||
"""Format albums for inspect_photo"""
|
||||
album_str = bold("Albums: ")
|
||||
album_names = []
|
||||
for album in photo.album_info:
|
||||
if album.folder_names:
|
||||
folder_str = "/".join(album.folder_names)
|
||||
album_names.append(f"{folder_str}/{album.title}")
|
||||
else:
|
||||
album_names.append(album.title)
|
||||
album_str += f"{', '.join(album_names) or '-'}"
|
||||
return album_str
|
||||
|
||||
|
||||
def format_path_link(path: str) -> str:
|
||||
"""Format a path as URI for display in terminal"""
|
||||
return f"[link={pathlib.Path(path).as_uri()}]{path}[/link]"
|
||||
|
||||
|
||||
def format_paths(photo: PhotoInfo) -> str:
|
||||
"""format photo paths for inspect_photo"""
|
||||
path_str = bold("Path original: ")
|
||||
path_str += f"[filepath]{format_path_link(photo.path)}[/]" if photo.path else "-"
|
||||
if photo.path_edited:
|
||||
path_str += "\n"
|
||||
path_str += bold("Path edited: ")
|
||||
path_str += f"[filepath]{format_path_link(photo.path_edited)}[/]"
|
||||
if photo.path_raw:
|
||||
path_str += "\n"
|
||||
path_str += bold("Path raw: ")
|
||||
path_str += f"[filepath]{format_path_link(photo.path_raw)}[/]"
|
||||
if photo.path_derivatives:
|
||||
path_str += "\n"
|
||||
path_str += bold("Path preview: ")
|
||||
path_str += f"[filepath]{format_path_link(photo.path_derivatives[0])}[/]"
|
||||
return path_str
|
||||
|
||||
|
||||
def format_exif_info(photo: PhotoInfo) -> str:
|
||||
"""Format exif_info for inspect_photo"""
|
||||
exif_str = bold("EXIF: ")
|
||||
exif = photo.exif_info
|
||||
if not exif:
|
||||
return f"{exif_str}-"
|
||||
|
||||
if exif.camera_make:
|
||||
exif_str += f"{exif.camera_make} "
|
||||
if exif.camera_model:
|
||||
exif_str += f"{exif.camera_model} "
|
||||
if exif.focal_length:
|
||||
exif_str += f"{exif.focal_length:.2f}mm "
|
||||
if exif.iso:
|
||||
exif_str += f"ISO {exif.iso} "
|
||||
if exif.flash_fired:
|
||||
exif_str += "Flash "
|
||||
if exif.exposure_bias is not None:
|
||||
exif_str += (
|
||||
f"{int(exif.exposure_bias)} ev "
|
||||
if exif.exposure_bias == int(exif.exposure_bias)
|
||||
else f"{exif.exposure_bias} ev "
|
||||
)
|
||||
if exif.aperture:
|
||||
exif_str += (
|
||||
f"ƒ{int(exif.aperture)} "
|
||||
if exif.aperture == int(exif.aperture)
|
||||
else f"ƒ{exif.aperture:.1f} "
|
||||
)
|
||||
if exif.shutter_speed:
|
||||
exif_str += f"{Fraction(exif.shutter_speed).limit_denominator(100_000)}s "
|
||||
if exif.bit_rate:
|
||||
exif_str += f"{exif.bit_rate} bit rate"
|
||||
if exif.sample_rate:
|
||||
exif_str += f"{exif.sample_rate} sample rate"
|
||||
if exif.fps:
|
||||
exif_str += f"{exif.fps or 0:.1f}FPS "
|
||||
if exif.duration:
|
||||
exif_str += f"{strftime('%H:%M:%S', gmtime(exif.duration or 0))} "
|
||||
if exif.codec:
|
||||
exif_str += f"{exif.codec}"
|
||||
if exif.track_format:
|
||||
exif_str += f"{exif.track_format}"
|
||||
return exif_str
|
||||
|
||||
|
||||
def get_photo_type(photo: PhotoInfo) -> str:
|
||||
"""Return a string describing the type of photo"""
|
||||
photo_type = "video" if photo.ismovie else "photo"
|
||||
if photo.has_raw:
|
||||
photo_type += " RAW+JPEG"
|
||||
if photo.israw:
|
||||
photo_type += " RAW"
|
||||
if photo.burst:
|
||||
photo_type += " burst"
|
||||
if photo.live_photo:
|
||||
photo_type += " live"
|
||||
if photo.selfie:
|
||||
photo_type += " selfie"
|
||||
if photo.panorama:
|
||||
photo_type += " panorama"
|
||||
if photo.hdr:
|
||||
photo_type += " HDR"
|
||||
if photo.screenshot:
|
||||
photo_type += " screenshot"
|
||||
if photo.slow_mo:
|
||||
photo_type += " slow-mo"
|
||||
if photo.time_lapse:
|
||||
photo_type += " time-lapse"
|
||||
if photo.portrait:
|
||||
photo_type += " portrait"
|
||||
return photo_type
|
||||
|
||||
|
||||
def start_text_detection(photo: PhotoInfo) -> Tuple[Process, Queue]:
|
||||
"""Start text detection process for a photo"""
|
||||
path_preview = photo.path_derivatives[0] if photo.path_derivatives else None
|
||||
path = photo.path_edited or photo.path or path_preview
|
||||
if not path:
|
||||
raise ValueError("No path to photo")
|
||||
queue = Queue()
|
||||
p = Process(
|
||||
target=_get_detected_text,
|
||||
args=(photo.uuid, path, photo.orientation, queue),
|
||||
)
|
||||
p.start()
|
||||
return (p, queue)
|
||||
|
||||
|
||||
def _get_detected_text(uuid: str, path: str, orientation: int, queue: Queue) -> None:
|
||||
"""Called by start_text_detection to run text detection in separate process"""
|
||||
try:
|
||||
if text := detect_text_in_photo(path, orientation):
|
||||
queue.put([uuid, " ".join(t[0] for t in text if t[1] > 0.5)])
|
||||
except Exception as e:
|
||||
queue.put([None, str(e)])
|
||||
|
||||
|
||||
def get_uuid_for_photos_selection() -> List[str]:
|
||||
"""Get the uuid for the first photo selected in Photos
|
||||
|
||||
Returns: tuple of (uuid, total_selected_photos)"""
|
||||
photoslib = PhotosLibrary()
|
||||
try:
|
||||
if photos := photoslib.selection:
|
||||
return photos[0].uuid, len(photos)
|
||||
except (ValueError, ScriptError) as e:
|
||||
if uuid := extract_uuid(str(e)):
|
||||
return uuid, 1
|
||||
else:
|
||||
raise e
|
||||
return None, 0
|
||||
|
||||
|
||||
def make_layout() -> Layout:
|
||||
"""Define the layout."""
|
||||
layout = Layout(name="root")
|
||||
|
||||
layout.split(
|
||||
Layout(name="main", ratio=1),
|
||||
Layout(name="status", size=1),
|
||||
Layout(name="footer", size=1),
|
||||
)
|
||||
return layout
|
||||
|
||||
|
||||
@click.command(name="inspect")
|
||||
@click.option("--detect-text", "-t", is_flag=True, help="Detect text in photos")
|
||||
@THEME_OPTION
|
||||
@DB_OPTION
|
||||
def photo_inspect(db, theme, detect_text):
|
||||
"""Interactively inspect photos selected in Photos.
|
||||
|
||||
Open Photos then run `osxphotos inspect` in the terminal.
|
||||
As you select a photo in Photos, inspect will display metadata about the photo.
|
||||
Press Ctrl+C to exit when done.
|
||||
Works best with a modern terminal like iTerm2 or Kitty.
|
||||
"""
|
||||
db = get_photos_db(db)
|
||||
if not db:
|
||||
raise click.UsageError(
|
||||
"Did not locate Photos database. Try running with --db option."
|
||||
)
|
||||
|
||||
theme = get_theme(theme)
|
||||
console = Console(theme=theme)
|
||||
|
||||
layout = make_layout()
|
||||
layout["footer"].update("Press Ctrl+C to quit")
|
||||
layout["main"].update(
|
||||
Panel("Loading Photos database, hold on...", title="Loading...")
|
||||
)
|
||||
|
||||
def loading_status(status: str):
|
||||
layout["status"].update(f"Loading database: {status}")
|
||||
|
||||
def update_status(status: str):
|
||||
layout["status"].update(status)
|
||||
|
||||
def update_detected_text(photo: PhotoInfo, uuid: str, text: str):
|
||||
global CURRENT_UUID
|
||||
if uuid == CURRENT_UUID:
|
||||
layout["main"].update(
|
||||
Panel(
|
||||
inspect_photo(photo, text),
|
||||
title=photo.title or photo.original_filename,
|
||||
)
|
||||
)
|
||||
|
||||
with Live(layout, console=console, refresh_per_second=10, screen=True):
|
||||
photosdb = PhotosDB(dbfile=db, verbose=loading_status)
|
||||
layout["main"].update(
|
||||
Panel("Select a photo in Photos to inspect", title="Select a photo")
|
||||
)
|
||||
processes = []
|
||||
global CURRENT_UUID
|
||||
detected_text_cache = {}
|
||||
last_uuid = None
|
||||
uuid = None
|
||||
total = 0
|
||||
while True:
|
||||
try:
|
||||
uuid, total = get_uuid_for_photos_selection()
|
||||
except Exception as e:
|
||||
layout["main"].update(Panel(f"Error: {e}", title="Error"))
|
||||
except KeyboardInterrupt:
|
||||
# allow Ctrl+C to quit
|
||||
break
|
||||
finally:
|
||||
if uuid and uuid != last_uuid:
|
||||
if total > 1:
|
||||
update_status(
|
||||
f"{total} photos selected; inspecting uuid={uuid}"
|
||||
)
|
||||
else:
|
||||
update_status(f"Inspecting uuid={uuid}")
|
||||
CURRENT_UUID = uuid
|
||||
if photo := photosdb.get_photo(uuid):
|
||||
layout["main"].update(
|
||||
Panel(
|
||||
inspect_photo(
|
||||
photo,
|
||||
detected_text=detected_text_cache.get(uuid, None),
|
||||
),
|
||||
title=photo.title or photo.original_filename,
|
||||
)
|
||||
)
|
||||
|
||||
# start text detection if requested
|
||||
if (
|
||||
detect_text
|
||||
and photo.isphoto
|
||||
and (
|
||||
photo.path
|
||||
or photo.path_edited
|
||||
or photo.path_derivatives
|
||||
)
|
||||
and uuid not in detected_text_cache
|
||||
):
|
||||
# can only detect text on photos if on disk
|
||||
# start text detection in separate process as it can take a few seconds
|
||||
update_detected_text_callback = functools.partial(
|
||||
update_detected_text, photo
|
||||
)
|
||||
process, queue = start_text_detection(photo)
|
||||
processes.append(
|
||||
[True, process, queue, update_detected_text_callback]
|
||||
)
|
||||
else:
|
||||
layout["main"].update(
|
||||
Panel(
|
||||
f"No photo found in database for uuid={uuid}",
|
||||
title="Error",
|
||||
)
|
||||
)
|
||||
last_uuid = uuid
|
||||
if not uuid:
|
||||
last_uuid = None
|
||||
layout["main"].update(
|
||||
Panel("Select a photo in Photos to inspect", title="Select a photo")
|
||||
)
|
||||
update_status("Select a photo in Photos to inspect")
|
||||
if detect_text:
|
||||
# check on text detection processes
|
||||
for _, values in enumerate(processes):
|
||||
alive, process, queue, update_detected_text_callback = values
|
||||
if alive:
|
||||
# process hasn't been marked as dead yet
|
||||
try:
|
||||
uuid, text = queue.get(False)
|
||||
update_detected_text_callback(uuid, text)
|
||||
detected_text_cache[uuid] = text
|
||||
process.join()
|
||||
# set alive = False
|
||||
values[0] = False
|
||||
except Empty:
|
||||
if not process.is_alive():
|
||||
# process has finished, nothing in the queue
|
||||
process.join()
|
||||
# set alive = False
|
||||
values[0] = False
|
||||
sleep(0.100)
|
||||
Binary file not shown.
@@ -6,8 +6,10 @@ import gzip
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import pickle
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
import time
|
||||
@@ -15,7 +17,7 @@ from contextlib import suppress
|
||||
from io import StringIO
|
||||
from sqlite3 import Error
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
from tenacity import retry, stop_after_attempt
|
||||
|
||||
@@ -30,7 +32,7 @@ __all__ = [
|
||||
"ExportDBTemp",
|
||||
]
|
||||
|
||||
OSXPHOTOS_EXPORTDB_VERSION = "7.0"
|
||||
OSXPHOTOS_EXPORTDB_VERSION = "7.1"
|
||||
OSXPHOTOS_ABOUT_STRING = f"Created by osxphotos version {__version__} (https://github.com/RhetTbull/osxphotos) on {datetime.datetime.now()}"
|
||||
|
||||
# max retry attempts for methods which use tenacity.retry
|
||||
@@ -171,6 +173,17 @@ class ExportDB:
|
||||
uuid = None
|
||||
return uuid
|
||||
|
||||
def get_files_for_uuid(self, uuid: str) -> List:
|
||||
"""query database for UUID and return list of files associated with UUID or empty list"""
|
||||
conn = self._conn
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"SELECT filepath FROM export_data WHERE uuid = ?",
|
||||
(uuid,),
|
||||
)
|
||||
results = c.fetchall()
|
||||
return [os.path.join(self.export_dir, r[0]) for r in results]
|
||||
|
||||
def get_photoinfo_for_uuid(self, uuid):
|
||||
"""returns the photoinfo JSON struct for a UUID"""
|
||||
conn = self._conn
|
||||
@@ -198,6 +211,35 @@ class ExportDB:
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def get_target_for_file(
|
||||
self, uuid: str, filename: Union[str, pathlib.Path]
|
||||
) -> Optional[str]:
|
||||
"""query database for file matching file name and return the matching filename if there is one;
|
||||
otherwise return None; looks for file.ext, file (1).ext, file (2).ext and so on to find the
|
||||
actual target name that was used to export filename
|
||||
|
||||
Returns: the matching filename or None if no match found
|
||||
"""
|
||||
conn = self._conn
|
||||
c = conn.cursor()
|
||||
filepath_normalized = self._normalize_filepath_relative(filename)
|
||||
filepath_stem = os.path.splitext(filepath_normalized)[0]
|
||||
c.execute(
|
||||
"SELECT uuid, filepath, filepath_normalized FROM export_data WHERE uuid = ? AND filepath_normalized LIKE ?",
|
||||
(
|
||||
uuid,
|
||||
f"{filepath_stem}%",
|
||||
),
|
||||
)
|
||||
results = c.fetchall()
|
||||
|
||||
for result in results:
|
||||
filepath_normalized = os.path.splitext(result[2])[0]
|
||||
if re.match(re.escape(filepath_stem) + r"(\s\(\d+\))?$", filepath_normalized):
|
||||
return os.path.join(self.export_dir, result[1])
|
||||
|
||||
return None
|
||||
|
||||
def get_previous_uuids(self):
|
||||
"""returns list of UUIDs of previously exported photos found in export database"""
|
||||
conn = self._conn
|
||||
@@ -470,6 +512,10 @@ class ExportDB:
|
||||
# create report_data table
|
||||
self._migrate_6_0_to_7_0(conn)
|
||||
|
||||
if version[1] < "7.1":
|
||||
# add timestamp to export_data
|
||||
self._migrate_7_0_to_7_1(conn)
|
||||
|
||||
conn.execute("VACUUM;")
|
||||
conn.commit()
|
||||
|
||||
@@ -687,6 +733,32 @@ class ExportDB:
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def _migrate_7_0_to_7_1(self, conn):
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute("""ALTER TABLE export_data ADD COLUMN timestamp DATETIME;""")
|
||||
c.execute(
|
||||
"""
|
||||
CREATE TRIGGER insert_timestamp_trigger
|
||||
AFTER INSERT ON export_data
|
||||
BEGIN
|
||||
UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
|
||||
END;
|
||||
"""
|
||||
)
|
||||
c.execute(
|
||||
"""
|
||||
CREATE TRIGGER update_timestamp_trigger
|
||||
AFTER UPDATE On export_data
|
||||
BEGIN
|
||||
UPDATE export_data SET timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
|
||||
END;
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def _perform_db_maintenace(self, conn):
|
||||
"""Perform database maintenance"""
|
||||
try:
|
||||
@@ -1048,6 +1120,21 @@ class ExportRecord:
|
||||
if not self._context_manager:
|
||||
conn.commit()
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
"""returns the timestamp value"""
|
||||
conn = self._conn
|
||||
c = conn.cursor()
|
||||
if row := c.execute(
|
||||
"SELECT timestamp FROM export_data WHERE filepath_normalized = ?;",
|
||||
(self._filepath_normalized,),
|
||||
).fetchone():
|
||||
return row[0]
|
||||
|
||||
raise ValueError(
|
||||
f"No timestamp found in database for {self._filepath_normalized}"
|
||||
)
|
||||
|
||||
def asdict(self):
|
||||
"""Return dict of self"""
|
||||
exifdata = json.loads(self.exifdata) if self.exifdata else None
|
||||
@@ -1056,6 +1143,7 @@ class ExportRecord:
|
||||
"filepath": self.filepath,
|
||||
"filepath_normalized": self.filepath_normalized,
|
||||
"uuid": self.uuid,
|
||||
"timestamp": self.timestamp,
|
||||
"digest": self.digest,
|
||||
"src_sig": self.src_sig,
|
||||
"dest_sig": self.dest_sig,
|
||||
|
||||
@@ -44,7 +44,13 @@ from .photokit import (
|
||||
from .phototemplate import RenderOptions
|
||||
from .rich_utils import add_rich_markup_tag
|
||||
from .uti import get_preferred_uti_extension
|
||||
from .utils import hexdigest, increment_filename, lineno, list_directory
|
||||
from .utils import (
|
||||
hexdigest,
|
||||
increment_filename,
|
||||
increment_filename_with_count,
|
||||
lineno,
|
||||
list_directory,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ExportError",
|
||||
@@ -488,7 +494,7 @@ class PhotoExporter:
|
||||
src = staged_files.edited if options.edited else staged_files.original
|
||||
|
||||
# get the right destination path depending on options.update, etc.
|
||||
dest = self._get_dest_path(src, dest, options)
|
||||
dest = self._get_dest_path(dest, options)
|
||||
|
||||
self._render_options.filepath = str(dest)
|
||||
all_results = ExportResults()
|
||||
@@ -642,12 +648,11 @@ class PhotoExporter:
|
||||
return edited_filename
|
||||
|
||||
def _get_dest_path(
|
||||
self, src: str, dest: pathlib.Path, options: ExportOptions
|
||||
self, dest: pathlib.Path, options: ExportOptions
|
||||
) -> pathlib.Path:
|
||||
"""If destination exists find match in ExportDB, on disk, or add (1), (2), and so on to filename to get a valid destination
|
||||
|
||||
Args:
|
||||
src (str): source file path
|
||||
dest (str): destination path
|
||||
options (ExportOptions): Export options
|
||||
|
||||
@@ -663,6 +668,10 @@ class PhotoExporter:
|
||||
f"destination exists ({dest}); overwrite={options.overwrite}, increment={options.increment}"
|
||||
)
|
||||
|
||||
# if overwrite, we don't care if the file exists or not
|
||||
if options.overwrite:
|
||||
return dest
|
||||
|
||||
# if not update or overwrite, check to see if file exists and if so, add (1), (2), etc
|
||||
# until we find one that works
|
||||
# Photos checks the stem and adds (1), (2), etc which avoids collision with sidecars
|
||||
@@ -675,29 +684,36 @@ class PhotoExporter:
|
||||
return pathlib.Path(increment_filename(dest))
|
||||
|
||||
# if update and file exists, need to check to see if it's the right file by checking export db
|
||||
if (options.update or options.force_update) and dest.exists() and src:
|
||||
if options.update or options.force_update:
|
||||
export_db = options.export_db
|
||||
# destination exists, check to see if destination is the right UUID
|
||||
dest_uuid = export_db.get_uuid_for_file(dest)
|
||||
if dest_uuid != self.photo.uuid:
|
||||
# not the right file, find the right one
|
||||
# find files that match "dest_name (*.ext" (e.g. "dest_name (1).jpg", "dest_name (2).jpg)", ...)
|
||||
dest_files = list_directory(
|
||||
dest.parent,
|
||||
startswith=f"{dest.stem} (",
|
||||
endswith=dest.suffix,
|
||||
include_path=True,
|
||||
)
|
||||
for file_ in dest_files:
|
||||
dest_uuid = export_db.get_uuid_for_file(file_)
|
||||
if dest_uuid == self.photo.uuid:
|
||||
dest = pathlib.Path(file_)
|
||||
break
|
||||
else:
|
||||
# increment the destination file
|
||||
dest = pathlib.Path(increment_filename(dest))
|
||||
if dest_uuid is None and not dest.exists():
|
||||
# destination doesn't exist in export db and doesn't exist on disk
|
||||
# so we can just use it
|
||||
return dest
|
||||
|
||||
# either dest was updated in the if clause above or not updated at all
|
||||
if dest_uuid == self.photo.uuid:
|
||||
# destination is the right file
|
||||
return dest
|
||||
|
||||
# either dest_uuid is wrong or file exists and there's no associated UUID, so find a name that matches
|
||||
# or create a new name if no match
|
||||
# find files that match "dest_name (*.ext" (e.g. "dest_name (1).jpg", "dest_name (2).jpg)", ...)
|
||||
# first, find all matching files in export db and see if there's a match
|
||||
if dest_target := export_db.get_target_for_file(self.photo.uuid, dest):
|
||||
# there's a match so use that
|
||||
return pathlib.Path(dest_target)
|
||||
|
||||
# no match so need to create a new name
|
||||
# increment the destination file until we find one that doesn't exist and doesn't match another uuid in the database
|
||||
count = 0
|
||||
dest, count = increment_filename_with_count(dest, count)
|
||||
count += 1
|
||||
while export_db.get_uuid_for_file(dest) is not None:
|
||||
dest, count = increment_filename_with_count(dest, count)
|
||||
return pathlib.Path(dest)
|
||||
|
||||
# fail safe...I can't think of a case that gets here
|
||||
return dest
|
||||
|
||||
def _should_update_photo(
|
||||
@@ -1036,9 +1052,9 @@ class PhotoExporter:
|
||||
|
||||
def _export_photo(
|
||||
self,
|
||||
src,
|
||||
dest,
|
||||
options,
|
||||
src: str,
|
||||
dest: pathlib.Path,
|
||||
options: ExportOptions,
|
||||
):
|
||||
"""Helper function for export()
|
||||
Does the actual copy or hardlink taking the appropriate
|
||||
@@ -1444,7 +1460,7 @@ class PhotoExporter:
|
||||
) -> ExportResults:
|
||||
"""Write exif metadata to src file using exiftool
|
||||
|
||||
Caution: This method modifies *src*, not *dest*,
|
||||
Caution: This method modifies *src*, not *dest*,
|
||||
so src must be a copy of the original file if you don't want the source modified;
|
||||
it also does not write to dest (dest is the intended destination for purposes of
|
||||
referencing the export database. This allows the exiftool update to be done on the
|
||||
|
||||
@@ -32,7 +32,7 @@ def detect_text(img_path: str, orientation: Optional[int] = None) -> List:
|
||||
orientation: optional EXIF orientation (if known, passing orientation may improve quality of results)
|
||||
"""
|
||||
if not vision:
|
||||
logging.warning(f"detect_text not implemented for this version of macOS")
|
||||
logging.warning("detect_text not implemented for this version of macOS")
|
||||
return []
|
||||
|
||||
with objc.autorelease_pool():
|
||||
@@ -47,18 +47,18 @@ def detect_text(img_path: str, orientation: Optional[int] = None) -> List:
|
||||
input_image = Quartz.CIImage.imageWithContentsOfURL_(input_url)
|
||||
|
||||
vision_options = NSDictionary.dictionaryWithDictionary_({})
|
||||
if orientation is not None:
|
||||
if not 1 <= orientation <= 8:
|
||||
raise ValueError("orientation must be between 1 and 8")
|
||||
vision_handler = Vision.VNImageRequestHandler.alloc().initWithCIImage_orientation_options_(
|
||||
input_image, orientation, vision_options
|
||||
)
|
||||
else:
|
||||
if orientation is None:
|
||||
vision_handler = (
|
||||
Vision.VNImageRequestHandler.alloc().initWithCIImage_options_(
|
||||
input_image, vision_options
|
||||
)
|
||||
)
|
||||
elif 1 <= orientation <= 8:
|
||||
vision_handler = Vision.VNImageRequestHandler.alloc().initWithCIImage_orientation_options_(
|
||||
input_image, orientation, vision_options
|
||||
)
|
||||
else:
|
||||
raise ValueError("orientation must be between 1 and 8")
|
||||
results = []
|
||||
handler = make_request_handler(results)
|
||||
vision_request = (
|
||||
|
||||
@@ -411,7 +411,7 @@ def normalize_unicode(value):
|
||||
|
||||
def increment_filename_with_count(
|
||||
filepath: Union[str, pathlib.Path], count: int = 0
|
||||
) -> str:
|
||||
) -> Tuple[str, int]:
|
||||
"""Return filename (1).ext, etc if filename.ext exists
|
||||
|
||||
If file exists in filename's parent folder with same stem as filename,
|
||||
@@ -447,6 +447,7 @@ def increment_filename(filepath: Union[str, pathlib.Path]) -> str:
|
||||
|
||||
Args:
|
||||
filepath: str or pathlib.Path; full path, including file name
|
||||
force: force the file count to increment by at least 1 even if filepath doesn't exist
|
||||
|
||||
Returns:
|
||||
new filepath (or same if not incremented)
|
||||
@@ -457,6 +458,13 @@ def increment_filename(filepath: Union[str, pathlib.Path]) -> str:
|
||||
return new_filepath
|
||||
|
||||
|
||||
def extract_increment_count_from_filename(filepath: Union[str, pathlib.Path]) -> int:
|
||||
"""Extract a count from end of file name if it exists or 0 if not; count takes forms file (1).ext, file (2).ext, etc."""
|
||||
filepath = str(filepath)
|
||||
match = re.search(r"(?s:.*)\((\d+)\)", filepath)
|
||||
return int(match[1]) if match else 0
|
||||
|
||||
|
||||
def expand_and_validate_filepath(path: str) -> str:
|
||||
"""validate and expand ~ in filepath, also un-escapes spaces
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ def test_export_db():
|
||||
assert db.get_uuid_for_file(filepath) is None
|
||||
db.create_file_record(filepath, uuid)
|
||||
assert db.get_uuid_for_file(filepath) == uuid
|
||||
assert db.get_files_for_uuid(uuid) == [filepath]
|
||||
|
||||
record = db.get_file_record(filepath)
|
||||
assert record.uuid == uuid
|
||||
@@ -142,6 +143,7 @@ def test_export_db_in_memory():
|
||||
assert record2.digest == DIGEST_DATA
|
||||
assert record2.src_sig == (7, 8, 9)
|
||||
assert record2.dest_sig == (10, 11, 12)
|
||||
assert dbram.get_files_for_uuid(uuid) == [filepath]
|
||||
|
||||
# change some values
|
||||
record2.photoinfo = INFO_DATA2
|
||||
|
||||
Reference in New Issue
Block a user