Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
128e84c7a4 | ||
|
|
4771207595 | ||
|
|
0eca3b9c13 | ||
|
|
afec845e1b | ||
|
|
64002044d2 | ||
|
|
4e40d4b74e | ||
|
|
63d515646b | ||
|
|
0a7575b889 | ||
|
|
c776f3070d | ||
|
|
3b789242aa | ||
|
|
dfcb99f377 | ||
|
|
8e9f27995b | ||
|
|
79e4b333e9 | ||
|
|
6d5af5c5e8 | ||
|
|
5a3e32a2b4 | ||
|
|
3473c2ece2 |
@@ -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.1'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.5'|
|
||||
|{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|
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -4,6 +4,38 @@ 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.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
|
||||
|
||||
- Implemented #689 [`4ec9f6d`](https://github.com/RhetTbull/osxphotos/commit/4ec9f6d3e606f72962e351432a64a24ff9153d87)
|
||||
- Unhid exportdb command [`5a9722b`](https://github.com/RhetTbull/osxphotos/commit/5a9722b37c8326bde7c6bde9870ce704aabb2a15)
|
||||
- Added example [`72af96b`](https://github.com/RhetTbull/osxphotos/commit/72af96b48ee7552d5d35e6f5d619c5b7a55050ab)
|
||||
|
||||
#### [v0.49.0](https://github.com/RhetTbull/osxphotos/compare/v0.48.8...v0.49.0)
|
||||
|
||||
> 15 May 2022
|
||||
|
||||
46
README.md
46
README.md
@@ -18,9 +18,10 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
|
||||
* [Supported operating systems](#supported-operating-systems)
|
||||
* [Installation](#installation)
|
||||
* [Command Line Usage](#command-line-usage)
|
||||
* [Command line examples](#command-line-examples)
|
||||
* [Command Line Examples](#command-line-examples)
|
||||
* [Tutorial](#tutorial)
|
||||
* [Command line reference: export](#command-line-reference-export)
|
||||
* [Command Line Reference: export](#command-line-reference-export)
|
||||
* [Files Created By OSXPhotos](#files-created-by-osxphotos)
|
||||
* [Package Interface](#package-interface)
|
||||
* [PhotosDB](#photosdb)
|
||||
* [PhotoInfo](#photoinfo)
|
||||
@@ -130,17 +131,17 @@ Usage: osxphotos [OPTIONS] COMMAND [ARGS]...
|
||||
osxphotos: query and export your Photos library
|
||||
|
||||
Options:
|
||||
--db <Photos database path> 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
|
||||
--json Print output in JSON format.
|
||||
-v, --version Show the version and exit.
|
||||
-h, --help Show this message and exit.
|
||||
--db PHOTOS_LIBRARY_PATH 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
|
||||
--json Print output in JSON format.
|
||||
-v, --version Show the version and exit.
|
||||
-h, --help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
about Print information about osxphotos including license.
|
||||
@@ -148,9 +149,12 @@ Commands:
|
||||
diff Compare two Photos databases and print out differences
|
||||
docs Open osxphotos documentation in your browser.
|
||||
dump Print list of all photos & associated info from the Photos...
|
||||
exiftool Run exiftool on previously exported files to update metadata.
|
||||
export Export photos from the Photos database.
|
||||
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...
|
||||
@@ -159,7 +163,7 @@ Commands:
|
||||
places Print out places found in the Photos library.
|
||||
query Query the Photos database using 1 or more search options; if...
|
||||
repl Run interactive osxphotos REPL shell (useful for debugging,...
|
||||
run Run a python file using same environment as osxphotos
|
||||
run Run a python file using same environment as osxphotos.
|
||||
snap Create snapshot of Photos database to use with diff command
|
||||
theme Manage osxphotos color themes.
|
||||
timewarp Adjust date/time/timezone of photos in Apple Photos.
|
||||
@@ -1846,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.1'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.49.5'
|
||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||
|
||||
The following substitutions may result in multiple values. Thus if specified
|
||||
@@ -2052,6 +2056,16 @@ not be called if the --dry-run flag is set.
|
||||
```
|
||||
<!-- OSXPHOTOS-EXPORT-USAGE:END -->
|
||||
|
||||
### Files Created By OSXPhotos
|
||||
|
||||
The OSXPhotos command line tool creates a number of files during the course of its execution.
|
||||
OSXPhotos adheres to the [XDG](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) standard for file locations.
|
||||
|
||||
* `$XDG_CONFIG_HOME` or `$HOME/.config`: `osxphotos` directory containing configuration files, for example color themes for colorized output.
|
||||
* `$XDG_DATA_HOME` or `$HOME/.local/share`: `osxphotos` directory containing local data files, for example, the help files displayed with `osxphotos docs`.
|
||||
* Current working dir: `osxphotos_crash.log` file containing the stack trace of the last crash if OSXPhotos encounters a fatal error during execution.
|
||||
* export directory (when running `osxphotos export` command): `.osxphotos_export.db` [SQLite](https://www.sqlite.org/index.html) database containing information needed to update an export and track metadata changes in exported photos. *Note*: This file may contain sensitive information such as locations and the names of persons in photos so if you are using `osxphotos export` to share with others, you may want to delete this file. You can also specify an alternate location for the export database using the `--exportdb` flag during export. See also `osxphotos help exportdb` for more information about built in utilities for working with the export database.
|
||||
|
||||
## Example uses of the package
|
||||
|
||||
```python
|
||||
@@ -4012,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.1'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.5'|
|
||||
|{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|
|
||||
|
||||
@@ -102,9 +102,12 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
|
||||
diff Compare two Photos databases and print out differences
|
||||
docs Open osxphotos documentation in your browser.
|
||||
dump Print list of all photos & associated info from the Photos...
|
||||
exiftool Run exiftool on previously exported files to update metadata.
|
||||
export Export photos from the Photos database.
|
||||
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: a77e6404d00931c687c8320621f91963
|
||||
config: 453784a6da675ee114829e25c5e16bfc
|
||||
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.1 documentation</title>
|
||||
<title>Overview: module code - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1 documentation</title>
|
||||
<title>osxphotos.export_db - osxphotos 0.49.4 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.1 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.4 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.4 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">
|
||||
@@ -211,7 +211,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">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span><span class="p">,</span> <span class="n">List</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 +226,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 +367,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>
|
||||
@@ -478,6 +489,20 @@
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">return</span> <span class="n">results</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_exported_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_exported_files">[docs]</a> <span class="k">def</span> <span class="nf">get_exported_files</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Returns tuple of (uuid, filepath) for all paths of all exported files tracked in the 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>
|
||||
<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">"SELECT uuid, filepath FROM export_data"</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">return</span>
|
||||
|
||||
<span class="k">while</span> <span class="n">row</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">fetchone</span><span class="p">():</span>
|
||||
<span class="k">yield</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</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">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||||
<span class="k">return</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.close"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.close">[docs]</a> <span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""close the database connection"""</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
@@ -495,7 +520,7 @@
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">dbfile</span><span class="p">):</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_db_connection</span><span class="p">(</span><span class="n">dbfile</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">conn</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">"Error getting connection to database </span><span class="si">{dbfile}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Error getting connection to database </span><span class="si">{</span><span class="n">dbfile</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_created</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">()</span>
|
||||
@@ -652,6 +677,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>
|
||||
|
||||
@@ -869,6 +898,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>
|
||||
@@ -1230,6 +1285,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>
|
||||
@@ -1238,6 +1308,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.0 documentation</title>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=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.0 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.2 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.0 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -931,7 +931,7 @@
|
||||
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EXPORT_OPTIONS_DIFFERENT</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
||||
<span class="n">current_exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">current_exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">rv</span> <span class="o">=</span> <span class="n">current_exifdata</span> <span class="o">!=</span> <span class="n">file_record</span><span class="o">.</span><span class="n">exifdata</span>
|
||||
<span class="c1"># if using exiftool, don't need to continue checking edited below</span>
|
||||
<span class="c1"># as exiftool will be used to update edited file</span>
|
||||
@@ -1339,7 +1339,7 @@
|
||||
<span class="c1"># point src to the tmp_file so that the original source is not modified</span>
|
||||
<span class="c1"># and the export grabs the new file</span>
|
||||
<span class="n">src</span> <span class="o">=</span> <span class="n">tmp_file</span>
|
||||
<span class="n">exif_results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_write_exif_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="n">exif_results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
@@ -1381,7 +1381,7 @@
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_signature</span><span class="p">:</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">dest_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span> <span class="o">!=</span> <span class="n">rec</span><span class="o">.</span><span class="n">digest</span><span class="p">:</span>
|
||||
<span class="n">results</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">=</span> <span class="p">[</span><span class="n">dest_str</span><span class="p">]</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">digest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
|
||||
@@ -1516,7 +1516,7 @@
|
||||
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
@@ -1533,7 +1533,7 @@
|
||||
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">tag_groups</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
@@ -1632,19 +1632,20 @@
|
||||
|
||||
<span class="k">return</span> <span class="n">results</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_write_exif_metadata_to_file</span><span class="p">(</span>
|
||||
<div class="viewcode-block" id="PhotoExporter.write_exiftool_metadata_to_file"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file">[docs]</a> <span class="k">def</span> <span class="nf">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">src</span><span class="p">,</span>
|
||||
<span class="n">dest</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
||||
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
||||
<span class="sd">"""Write exif metadata to file using exiftool</span>
|
||||
<span class="sd">"""Write exif metadata to src file using exiftool</span>
|
||||
|
||||
<span class="sd"> Note: this method modifies src so src must be a copy of the original file;</span>
|
||||
<span class="sd"> Caution: This method modifies *src*, not *dest*, </span>
|
||||
<span class="sd"> so src must be a copy of the original file if you don't want the source modified;</span>
|
||||
<span class="sd"> it also does not write to dest (dest is the intended destination for purposes of</span>
|
||||
<span class="sd"> referencing the export database. This allows the exiftool update to be done on the</span>
|
||||
<span class="sd"> local machine prior to being copied to the export destination which may be on a</span>
|
||||
<span class="sd"> network drive or other slower external storage."""</span>
|
||||
<span class="sd"> network drive or other slower external storage)."""</span>
|
||||
|
||||
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
||||
<span class="n">exiftool_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
||||
@@ -1676,7 +1677,7 @@
|
||||
|
||||
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exif_updated</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">to_touch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">exiftool_results</span>
|
||||
<span class="k">return</span> <span class="n">exiftool_results</span></div>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_should_run_exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||
<span class="sd">"""Return True if exiftool should be run to update metadata"""</span>
|
||||
@@ -1687,7 +1688,7 @@
|
||||
<span class="n">old_data</span> <span class="o">=</span> <span class="n">exif_record</span><span class="o">.</span><span class="n">exifdata</span> <span class="k">if</span> <span class="n">exif_record</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
<span class="k">if</span> <span class="n">old_data</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">old_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">old_data</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">))</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">))</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">current_data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">old_data</span> <span class="o">!=</span> <span class="n">current_data</span><span class="p">:</span>
|
||||
<span class="n">files_are_different</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
@@ -2027,7 +2028,7 @@
|
||||
<span class="k">pass</span>
|
||||
<span class="k">return</span> <span class="n">persons</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<div class="viewcode-block" id="PhotoExporter.exiftool_json_sidecar"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">[docs]</a> <span class="k">def</span> <span class="nf">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">tag_groups</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">,</span>
|
||||
@@ -2079,7 +2080,7 @@
|
||||
<span class="n">exif_new</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
||||
<span class="n">exif</span> <span class="o">=</span> <span class="n">exif_new</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">exif</span><span class="p">])</span>
|
||||
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">exif</span><span class="p">])</span></div>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_xmp_sidecar</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
|
||||
@@ -320,7 +320,7 @@ Template Substitutions
|
||||
* - {crlf}
|
||||
- a carriage return + line feed: '\r\n'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.49.1'
|
||||
- The osxphotos version, e.g. '0.49.5'
|
||||
* - {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.1',
|
||||
VERSION: '0.49.5',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
182
docs/cli.html
182
docs/cli.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.1 documentation</title>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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">
|
||||
@@ -343,6 +343,138 @@ library specified by –db to the database specified by DB2</p>
|
||||
<dd><p>Optional argument(s)</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-exiftool">
|
||||
<h3>exiftool<a class="headerlink" href="#osxphotos-exiftool" title="Permalink to this headline">#</a></h3>
|
||||
<p>Run exiftool on previously exported files to update metadata.</p>
|
||||
<p>If you previously exported photos with <cite>osxphotos export</cite> but did not include the
|
||||
<cite>–exiftool</cite> option and you now want to update the metadata of the exported files with
|
||||
exiftool, you can use this command to do so.</p>
|
||||
<p>If you simply re-run the <cite>osxphotos export</cite> with <cite>–update</cite> and <cite>–exiftool</cite>, osxphotos will
|
||||
re-export all photos because it will detect that the previously exported photos do not have the
|
||||
exiftool metadata updates. This command will run exiftool on the previously exported photos
|
||||
to update all metadata then will update the export database so that using <cite>–exiftool –update</cite>
|
||||
with <cite>osxphotos export</cite> in the future will work correctly and not unnecessarily re-export photos.</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos exiftool <span class="o">[</span>OPTIONS<span class="o">]</span> EXPORT_DIRECTORY
|
||||
</pre></div>
|
||||
</div>
|
||||
<p class="rubric">Options</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-db-config">
|
||||
<span class="sig-name descname"><span class="pre">--db-config</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-db-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Load configuration options from the export database to match the last export; If any other command line options are used in conjunction with –db-config, they will override the corresponding values loaded from the export database; see also –load-config.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-load-config">
|
||||
<span class="sig-name descname"><span class="pre">--load-config</span></span><span class="sig-prename descclassname"> <span class="pre"><CONFIG_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-load-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Load options from file as written with –save-config. If any other command line options are used in conjunction with –load-config, they will override the corresponding values in the config file; see also –db-config.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-save-config">
|
||||
<span class="sig-name descname"><span class="pre">--save-config</span></span><span class="sig-prename descclassname"> <span class="pre"><CONFIG_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-save-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Save options to file for use with –load-config. File format is TOML.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-path">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-path</span></span><span class="sig-prename descclassname"> <span class="pre"><EXIFTOOL_PATH></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-path" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optionally specify path to exiftool; if not provided, will look for exiftool in $PATH.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-option">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-option</span></span><span class="sig-prename descclassname"> <span class="pre"><OPTION></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-option" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional flag/option to pass to exiftool when using –exiftool. For example, –exiftool-option ‘-m’ to ignore minor warnings. Specify these as you would on the exiftool command line. See exiftool docs at <a class="reference external" href="https://exiftool.org/exiftool_pod.html">https://exiftool.org/exiftool_pod.html</a> for full list of options. More than one option may be specified by repeating the option, e.g. –exiftool-option ‘-m’ –exiftool-option ‘-F’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-merge-keywords">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-merge-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-merge-keywords" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Merge any keywords found in the original file with keywords used for ‘–exiftool’ and ‘–sidecar’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-merge-persons">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-merge-persons</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-merge-persons" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Merge any persons found in the original file with persons used for ‘–exiftool’ and ‘–sidecar’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-ignore-date-modified">
|
||||
<span class="sig-name descname"><span class="pre">--ignore-date-modified</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-ignore-date-modified" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –exiftool or –sidecar, will ignore the photo modification date and set EXIF:ModifyDate to EXIF:DateTimeOriginal; this is consistent with how Photos handles the EXIF:ModifyDate tag.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-person-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--person-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-person-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Use person in image as keyword/tag when exporting metadata.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-album-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--album-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-album-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Use album name as keyword/tag when exporting metadata.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-keyword-template">
|
||||
<span class="sig-name descname"><span class="pre">--keyword-template</span></span><span class="sig-prename descclassname"> <span class="pre"><TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-keyword-template" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>For use with –exiftool, –sidecar; specify a template string to use as keyword in the form ‘{name,DEFAULT}’ This is the same format as –directory. For example, if you wanted to add the full path to the folder and album photo is contained in as a keyword when exporting you could specify –keyword-template “{folder_album}” You may specify more than one template, for example –keyword-template “{folder_album}” –keyword-template “{created.year}”. See ‘–replace-keywords’ and Templating System below.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-replace-keywords">
|
||||
<span class="sig-name descname"><span class="pre">--replace-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-replace-keywords" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Replace keywords with any values specified with –keyword-template. By default, –keyword-template will add keywords to any keywords already associated with the photo. If –replace-keywords is specified, values from –keyword-template will replace any existing keywords instead of adding additional keywords.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-description-template">
|
||||
<span class="sig-name descname"><span class="pre">--description-template</span></span><span class="sig-prename descclassname"> <span class="pre"><TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-description-template" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>For use with –exiftool, –sidecar; specify a template string to use as description in the form ‘{name,DEFAULT}’ This is the same format as –directory. For example, if you wanted to append ‘exported with osxphotos on [today’s date]’ to the description, you could specify –description-template “{descr} exported with osxphotos on {today.date}” See Templating System below.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exportdb">
|
||||
<span class="sig-name descname"><span class="pre">--exportdb</span></span><span class="sig-prename descclassname"> <span class="pre"><exportdb></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exportdb" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional path to export database (if not in the default location in the export directory).</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-report">
|
||||
<span class="sig-name descname"><span class="pre">--report</span></span><span class="sig-prename descclassname"> <span class="pre"><REPORT_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-report" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Write a report of all files that were exported. The extension of the report filename will be used to determine the format. Valid extensions are: .csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). 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>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-append">
|
||||
<span class="sig-name descname"><span class="pre">--append</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-append" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –report, add data to existing report file instead of overwriting it. See also –report.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-V">
|
||||
<span id="cmdoption-osxphotos-exiftool-v"></span><span id="cmdoption-osxphotos-exiftool-verbose"></span><span class="sig-name descname"><span class="pre">-V</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">--verbose</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-V" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print verbose output.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-timestamp">
|
||||
<span class="sig-name descname"><span class="pre">--timestamp</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-timestamp" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Add time stamp to verbose output</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-dry-run">
|
||||
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-dry-run" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Run in dry-run mode (don’t actually update files), e.g. for use with –update-signatures.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-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-exiftool-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-exiftool-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-exiftool-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>
|
||||
<p class="rubric">Arguments</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">
|
||||
<span id="cmdoption-osxphotos-exiftool-arg-export-directory"></span><span class="sig-name descname"><span class="pre">EXPORT_DIRECTORY</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Required argument</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-export">
|
||||
<h3>export<a class="headerlink" href="#osxphotos-export" title="Permalink to this headline">#</a></h3>
|
||||
<p>Export photos from the Photos database.
|
||||
@@ -1146,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>
|
||||
@@ -1229,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>
|
||||
@@ -2568,10 +2742,12 @@ Commands:
|
||||
<li><a class="reference internal" href="#osxphotos-diff">diff</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-docs">docs</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-dump">dump</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-exiftool">exiftool</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-export">export</a></li>
|
||||
<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.1 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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">
|
||||
@@ -279,6 +279,8 @@
|
||||
--album-keyword
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-album-keyword">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-album-keyword">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -286,6 +288,8 @@
|
||||
--append
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-append">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-append">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-append">osxphotos-exportdb command line option</a>
|
||||
@@ -392,10 +396,14 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-db">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-db">osxphotos-dump command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<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>
|
||||
@@ -410,6 +418,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-db">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-snap-db">osxphotos-snap command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--db-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -467,7 +482,16 @@
|
||||
--description-template
|
||||
|
||||
<ul>
|
||||
<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>
|
||||
@@ -488,6 +512,8 @@
|
||||
--dry-run
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-dry-run">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-dry-run">osxphotos-exportdb command line option</a>
|
||||
@@ -558,6 +584,8 @@
|
||||
--exiftool-merge-keywords
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-keywords">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-merge-keywords">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -565,6 +593,8 @@
|
||||
--exiftool-merge-persons
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-persons">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-merge-persons">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -572,6 +602,8 @@
|
||||
--exiftool-option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-option">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-option">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -579,6 +611,8 @@
|
||||
--exiftool-path
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-path">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-path">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-e">osxphotos-timewarp command line option</a>
|
||||
@@ -609,6 +643,8 @@
|
||||
--exportdb
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exportdb">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exportdb">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -788,6 +824,8 @@
|
||||
--ignore-date-modified
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-ignore-date-modified">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-ignore-date-modified">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -897,6 +935,8 @@
|
||||
--keyword-template
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-keyword-template">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-keyword-template">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -954,6 +994,8 @@
|
||||
--load-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-load-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-load-config">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -1358,6 +1400,8 @@
|
||||
--person-keyword
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-person-keyword">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-person-keyword">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -1492,6 +1536,8 @@
|
||||
--replace-keywords
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -1499,6 +1545,8 @@
|
||||
--report
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-report">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-report">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-report">osxphotos-exportdb command line option</a>
|
||||
@@ -1522,6 +1570,8 @@
|
||||
--save-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-save-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-save-config">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-save-config">osxphotos-exportdb command line option</a>
|
||||
@@ -1670,7 +1720,11 @@
|
||||
--theme
|
||||
|
||||
<ul>
|
||||
<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>
|
||||
@@ -1704,6 +1758,8 @@
|
||||
--timestamp
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-timestamp">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-timestamp">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-timestamp">osxphotos-timewarp command line option</a>
|
||||
@@ -1827,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>
|
||||
@@ -1838,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>
|
||||
@@ -1852,6 +1922,8 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
|
||||
</li>
|
||||
@@ -2025,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>
|
||||
@@ -2040,6 +2114,8 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
|
||||
</li>
|
||||
@@ -2306,14 +2382,16 @@
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions.exiftool_flags">exiftool_flags (osxphotos.ExportOptions attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">exiftool_json_sidecar() (osxphotos.PhotoExporter method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.export">export() (osxphotos.PhotoExporter method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.export">(osxphotos.PhotoInfo method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions.export_as_hardlink">export_as_hardlink (osxphotos.ExportOptions attribute)</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -2327,6 +2405,13 @@
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.export_dir">export_dir (osxphotos.ExportDB property)</a>
|
||||
</li>
|
||||
<li>
|
||||
EXPORT_DIRECTORY
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB">ExportDB (class in osxphotos)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions">ExportOptions (class in osxphotos)</a>
|
||||
@@ -2402,19 +2487,23 @@
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.get_db_connection">get_db_connection() (osxphotos.PhotosDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_export_results">get_export_results() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<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>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.get_photo">get_photo() (osxphotos.PhotosDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_photo_video_type">get_photo_video_type() (osxphotos.PhotoTemplate method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid">get_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid">get_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_previous_uuids">get_previous_uuids() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_template_value">get_template_value() (osxphotos.PhotoTemplate method)</a>
|
||||
@@ -2811,6 +2900,57 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-json">--json</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
osxphotos-exiftool command line option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-album-keyword">--album-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-append">--append</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">--db</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db-config">--db-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-description-template">--description-template</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">--dry-run</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-keywords">--exiftool-merge-keywords</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-persons">--exiftool-merge-persons</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-option">--exiftool-option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-path">--exiftool-path</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exportdb">--exportdb</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-ignore-date-modified">--ignore-date-modified</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-keyword-template">--keyword-template</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-load-config">--load-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-person-keyword">--person-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">--replace-keywords</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-report">--report</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-save-config">--save-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">--theme</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-timestamp">--timestamp</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">--verbose</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">-V</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">EXPORT_DIRECTORY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -3137,6 +3277,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>
|
||||
@@ -3167,6 +3311,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>
|
||||
@@ -3202,6 +3359,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
|
||||
|
||||
@@ -3404,8 +3563,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
osxphotos-repl command line option
|
||||
|
||||
@@ -4150,6 +4307,8 @@
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.width">width (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file">write_exiftool_metadata_to_file() (osxphotos.PhotoExporter method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
@@ -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.1 documentation</title>
|
||||
<title>osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="#"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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">
|
||||
@@ -237,10 +237,12 @@
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-diff">diff</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-docs">docs</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-dump">dump</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-exiftool">exiftool</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-export">export</a></li>
|
||||
<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.1 documentation</title>
|
||||
<title>OSXPhotos - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1 documentation</title>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1 documentation</title>
|
||||
<title>OSXPhotos python API - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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">
|
||||
@@ -412,12 +412,22 @@ If called in context manager, returns True (execution is delayed until exiting c
|
||||
</dl>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_exported_files">
|
||||
<span class="sig-name descname"><span class="pre">get_exported_files</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_exported_files"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_exported_files" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Returns tuple of (uuid, filepath) for all paths of all exported files tracked in the database</p>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_file_record">
|
||||
<span class="sig-name descname"><span class="pre">get_file_record</span></span><span class="sig-paren">(</span><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">pathlib.Path</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">str</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">osxphotos.export_db.ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_file_record" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>get info for filename and uuid</p>
|
||||
<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>
|
||||
@@ -1137,6 +1147,49 @@ Highest quality face is result[0] and lowest quality face is result[n]</p>
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">osxphotos.</span></span><span class="sig-name descname"><span class="pre">PhotoExporter</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">photo</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#osxphotos.PhotoInfo" title="osxphotos.PhotoInfo"><span class="pre">PhotoInfo</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">tmpdir</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><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 class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Export a photo</p>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.PhotoExporter.exiftool_json_sidecar">
|
||||
<span class="sig-name descname"><span class="pre">exiftool_json_sidecar</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">options</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><a class="reference internal" href="#osxphotos.ExportOptions" title="osxphotos.photoexporter.ExportOptions"><span class="pre">osxphotos.photoexporter.ExportOptions</span></a><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">tag_groups</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</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">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 class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter.exiftool_json_sidecar"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter.exiftool_json_sidecar" title="Permalink to this definition">#</a></dt>
|
||||
<dd><dl class="simple">
|
||||
<dt>Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.</dt><dd><p>Does not include all the EXIF fields as those are likely already in the image.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><ul class="simple">
|
||||
<li><p><strong>options</strong> (<a class="reference internal" href="#osxphotos.ExportOptions" title="osxphotos.ExportOptions"><em>ExportOptions</em></a>) – options for export</p></li>
|
||||
<li><p><strong>tag_groups</strong> (<em>bool</em><em>, </em><em>default=True</em>) – if True, include tag groups in the output</p></li>
|
||||
<li><p><strong>filename</strong> (<em>str</em>) – name of source image file (without path); if not None, exiftool JSON signature will be included; if None, signature will not be included</p></li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>Returns: dict with exiftool tags / values</p>
|
||||
<dl class="simple">
|
||||
<dt>Exports the following:</dt><dd><p>EXIF:ImageDescription
|
||||
XMP:Description (may include template)
|
||||
IPTC:CaptionAbstract
|
||||
XMP:Title
|
||||
IPTC:ObjectName
|
||||
XMP:TagsList
|
||||
IPTC:Keywords (may include album name, person name, or template)
|
||||
XMP:Subject (set to keywords + person)
|
||||
XMP:PersonInImage
|
||||
EXIF:GPSLatitudeRef, EXIF:GPSLongitudeRef
|
||||
EXIF:GPSLatitude, EXIF:GPSLongitude
|
||||
EXIF:GPSPosition
|
||||
EXIF:DateTimeOriginal
|
||||
EXIF:OffsetTimeOriginal
|
||||
EXIF:ModifyDate
|
||||
IPTC:DigitalCreationDate
|
||||
IPTC:DateCreated
|
||||
QuickTime:CreationDate
|
||||
QuickTime:CreateDate (UTC)
|
||||
QuickTime:ModifyDate (UTC)
|
||||
QuickTime:GPSCoordinates
|
||||
UserData:GPSCoordinates</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.PhotoExporter.export">
|
||||
<span class="sig-name descname"><span class="pre">export</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">dest</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">options</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Optional</span><span class="p"><span class="pre">[</span></span><a class="reference internal" href="#osxphotos.ExportOptions" title="osxphotos.photoexporter.ExportOptions"><span class="pre">osxphotos.photoexporter.ExportOptions</span></a><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><a class="reference internal" href="#osxphotos.ExportResults" title="osxphotos.photoexporter.ExportResults"><span class="pre">osxphotos.photoexporter.ExportResults</span></a></span></span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter.export"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter.export" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Export photo</p>
|
||||
@@ -1167,6 +1220,17 @@ reference PhotoInfo.path_edited</p></li>
|
||||
</dl>
|
||||
</div>
|
||||
</dd></dl>
|
||||
<dl class="py method">
|
||||
<dt class="sig sig-object py" id="osxphotos.PhotoExporter.write_exiftool_metadata_to_file">
|
||||
<span class="sig-name descname"><span class="pre">write_exiftool_metadata_to_file</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">src</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">dest</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">options</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#osxphotos.ExportOptions" title="osxphotos.photoexporter.ExportOptions"><span class="pre">osxphotos.photoexporter.ExportOptions</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">→</span> <span class="sig-return-typehint"><a class="reference internal" href="#osxphotos.ExportResults" title="osxphotos.photoexporter.ExportResults"><span class="pre">osxphotos.photoexporter.ExportResults</span></a></span></span><a class="reference internal" href="_modules/osxphotos/photoexporter.html#PhotoExporter.write_exiftool_metadata_to_file"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.PhotoExporter.write_exiftool_metadata_to_file" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Write exif metadata to src file using exiftool</p>
|
||||
<p>Caution: This method modifies <em>src</em>, not <em>dest</em>,
|
||||
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
|
||||
local machine prior to being copied to the export destination which may be on a
|
||||
network drive or other slower external storage).</p>
|
||||
</dd></dl>
|
||||
</dd></dl>
|
||||
<dl class="py class">
|
||||
<dt class="sig sig-object py" id="osxphotos.PhotoInfo">
|
||||
|
||||
@@ -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.1 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.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1 documentation</title>
|
||||
<title>OSXPhotos Template System - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1’</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.49.5’</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.1 documentation</title>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.49.5 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.1 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.5 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.1 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.5 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.1'
|
||||
- The osxphotos version, e.g. '0.49.5'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
90
examples/photo_inspect.py
Normal file
90
examples/photo_inspect.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Print information about one or more items selected in Photos; run with `osxphotos run photo_inspect.py`"""
|
||||
|
||||
from time import sleep
|
||||
|
||||
import bitmath
|
||||
from photoscript import PhotosLibrary
|
||||
from rich import print
|
||||
|
||||
from osxphotos import PhotoInfo, PhotosDB
|
||||
from osxphotos.utils import dd_to_dms_str
|
||||
|
||||
|
||||
def get_photo_type(photo: PhotoInfo):
|
||||
"""Return a string describing the type of photo"""
|
||||
if photo.ismovie:
|
||||
photo_type = "video"
|
||||
else:
|
||||
raw = "RAW+JPEG " if photo.has_raw else "RAW " if photo.israw else ""
|
||||
photo_type = f"{raw}photo"
|
||||
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 inspect_photo(photo: PhotoInfo):
|
||||
"""Print info about an osxphotos PhotoInfo object"""
|
||||
|
||||
properties = [
|
||||
f"filename: {photo.original_filename}",
|
||||
f"type: {get_photo_type(photo)}",
|
||||
f"uuid: {photo.uuid}",
|
||||
f"date: {photo.date.isoformat()}",
|
||||
f"dimensions: {photo.height} x {photo.width}",
|
||||
f"file size: {bitmath.Byte(photo.original_filesize).to_MB()}",
|
||||
f"title: {photo.title or '-'}",
|
||||
f"description: {photo.description or '-'}",
|
||||
f"edited: {'✔' if photo.hasadjustments else '-'}",
|
||||
f"keywords: {', '.join(photo.keywords) or '-'}",
|
||||
f"persons: {', '.join(photo.persons) or '-'}",
|
||||
f"location: {', '.join(dd_to_dms_str(*photo.location)) if photo.location[0] else '-'}",
|
||||
f"place: {photo.place.name if photo.place else '-'}",
|
||||
f"categories: {', '.join(photo.labels) or '-'}",
|
||||
f"albums: {', '.join(photo.albums) or '-'}",
|
||||
f"favorite: {'♥' if photo.favorite else '-'}",
|
||||
]
|
||||
if photo.exif_info:
|
||||
properties.extend(
|
||||
[
|
||||
f"camera: {photo.exif_info.camera_make or '-'} {photo.exif_info.camera_model or '-'}",
|
||||
f"lens: {photo.exif_info.lens_model or '-'}",
|
||||
]
|
||||
)
|
||||
for property in properties:
|
||||
print(property)
|
||||
print("-" * 20)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Loading Photos Library...")
|
||||
photosdb = PhotosDB()
|
||||
photoslib = PhotosLibrary()
|
||||
|
||||
# keep track of last seen UUIDs so we don't print duplicates
|
||||
last_uuids = []
|
||||
print("Select one or more photos in Photos (press Ctrl+C to quit)")
|
||||
while True:
|
||||
if photos := photoslib.selection:
|
||||
uuids = sorted([photo.uuid for photo in photos])
|
||||
if uuids != last_uuids:
|
||||
for photo in photos:
|
||||
photoinfo = photosdb.get_photo(photo.uuid)
|
||||
inspect_photo(photoinfo)
|
||||
last_uuids = uuids
|
||||
sleep(0.200)
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.49.1"
|
||||
__version__ = "0.49.5"
|
||||
|
||||
@@ -48,6 +48,7 @@ from .cli import cli_main
|
||||
from .common import get_photos_db, load_uuid_from_file
|
||||
from .debug_dump import debug_dump
|
||||
from .dump import dump
|
||||
from .exiftool_cli import exiftool
|
||||
from .export import export
|
||||
from .exportdb import exportdb
|
||||
from .grep import grep
|
||||
@@ -58,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
|
||||
@@ -74,6 +76,7 @@ __all__ = [
|
||||
"debug_dump",
|
||||
"diff",
|
||||
"dump",
|
||||
"exiftool_cli",
|
||||
"export",
|
||||
"exportdb",
|
||||
"grep",
|
||||
@@ -86,6 +89,7 @@ __all__ = [
|
||||
"list_libraries",
|
||||
"load_uuid_from_file",
|
||||
"persons",
|
||||
"photo_inspect",
|
||||
"places",
|
||||
"query",
|
||||
"repl",
|
||||
|
||||
@@ -10,6 +10,7 @@ from .common import DB_OPTION, JSON_OPTION, OSXPHOTOS_HIDDEN
|
||||
from .debug_dump import debug_dump
|
||||
from .docs import docs
|
||||
from .dump import dump
|
||||
from .exiftool_cli import exiftool
|
||||
from .export import export
|
||||
from .exportdb import exportdb
|
||||
from .grep import grep
|
||||
@@ -20,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
|
||||
@@ -67,6 +69,7 @@ for command in [
|
||||
diff,
|
||||
docs,
|
||||
dump,
|
||||
exiftool,
|
||||
export,
|
||||
exportdb,
|
||||
grep,
|
||||
@@ -77,6 +80,7 @@ for command in [
|
||||
labels,
|
||||
list_libraries,
|
||||
persons,
|
||||
photo_inspect,
|
||||
places,
|
||||
query,
|
||||
repl,
|
||||
|
||||
417
osxphotos/cli/exiftool_cli.py
Normal file
417
osxphotos/cli/exiftool_cli.py
Normal file
@@ -0,0 +1,417 @@
|
||||
"""exiftool command for osxphotos CLI to update an previous export with exiftool metadata"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Callable
|
||||
|
||||
import click
|
||||
|
||||
from osxphotos import PhotosDB
|
||||
from osxphotos._constants import OSXPHOTOS_EXPORT_DB
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.configoptions import ConfigOptions, ConfigOptionsLoadError
|
||||
from osxphotos.export_db import ExportDB, ExportDBInMemory
|
||||
from osxphotos.export_db_utils import export_db_get_config
|
||||
from osxphotos.fileutil import FileUtil, FileUtilNoOp
|
||||
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
|
||||
from osxphotos.utils import pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
rich_click_echo,
|
||||
rich_echo_error,
|
||||
set_rich_console,
|
||||
set_rich_theme,
|
||||
set_rich_timestamp,
|
||||
)
|
||||
from .color_themes import get_theme
|
||||
from .common import DB_OPTION, THEME_OPTION, get_photos_db
|
||||
from .export import export, render_and_validate_report
|
||||
from .param_types import ExportDBType, TemplateString
|
||||
from .report_writer import ReportWriterNoOp, report_writer_factory
|
||||
from .rich_progress import rich_progress
|
||||
from .verbose import get_verbose_console, verbose_print
|
||||
|
||||
|
||||
@click.command(name="exiftool")
|
||||
@click.option(
|
||||
"--db-config",
|
||||
is_flag=True,
|
||||
help="Load configuration options from the export database to match the last export; "
|
||||
"If any other command line options are used in conjunction with --db-config, "
|
||||
"they will override the corresponding values loaded from the export database; "
|
||||
"see also --load-config.",
|
||||
)
|
||||
@click.option(
|
||||
"--load-config",
|
||||
required=False,
|
||||
metavar="CONFIG_FILE",
|
||||
default=None,
|
||||
help=(
|
||||
"Load options from file as written with --save-config. "
|
||||
"If any other command line options are used in conjunction with --load-config, "
|
||||
"they will override the corresponding values in the config file; "
|
||||
"see also --db-config."
|
||||
),
|
||||
type=click.Path(exists=True),
|
||||
)
|
||||
@click.option(
|
||||
"--save-config",
|
||||
required=False,
|
||||
metavar="CONFIG_FILE",
|
||||
default=None,
|
||||
help="Save options to file for use with --load-config. File format is TOML. ",
|
||||
type=click.Path(),
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-path",
|
||||
metavar="EXIFTOOL_PATH",
|
||||
type=click.Path(exists=True),
|
||||
help="Optionally specify path to exiftool; if not provided, will look for exiftool in $PATH.",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-option",
|
||||
multiple=True,
|
||||
metavar="OPTION",
|
||||
help="Optional flag/option to pass to exiftool when using --exiftool. "
|
||||
"For example, --exiftool-option '-m' to ignore minor warnings. "
|
||||
"Specify these as you would on the exiftool command line. "
|
||||
"See exiftool docs at https://exiftool.org/exiftool_pod.html for full list of options. "
|
||||
"More than one option may be specified by repeating the option, e.g. "
|
||||
"--exiftool-option '-m' --exiftool-option '-F'. ",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-merge-keywords",
|
||||
is_flag=True,
|
||||
help="Merge any keywords found in the original file with keywords used for '--exiftool' and '--sidecar'.",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-merge-persons",
|
||||
is_flag=True,
|
||||
help="Merge any persons found in the original file with persons used for '--exiftool' and '--sidecar'.",
|
||||
)
|
||||
@click.option(
|
||||
"--ignore-date-modified",
|
||||
is_flag=True,
|
||||
help="If used with --exiftool or --sidecar, will ignore the photo "
|
||||
"modification date and set EXIF:ModifyDate to EXIF:DateTimeOriginal; "
|
||||
"this is consistent with how Photos handles the EXIF:ModifyDate tag.",
|
||||
)
|
||||
@click.option(
|
||||
"--person-keyword",
|
||||
is_flag=True,
|
||||
help="Use person in image as keyword/tag when exporting metadata.",
|
||||
)
|
||||
@click.option(
|
||||
"--album-keyword",
|
||||
is_flag=True,
|
||||
help="Use album name as keyword/tag when exporting metadata.",
|
||||
)
|
||||
@click.option(
|
||||
"--keyword-template",
|
||||
metavar="TEMPLATE",
|
||||
multiple=True,
|
||||
default=None,
|
||||
help="For use with --exiftool, --sidecar; specify a template string to use as "
|
||||
"keyword in the form '{name,DEFAULT}' "
|
||||
"This is the same format as --directory. For example, if you wanted to add "
|
||||
"the full path to the folder and album photo is contained in as a keyword when exporting "
|
||||
'you could specify --keyword-template "{folder_album}" '
|
||||
'You may specify more than one template, for example --keyword-template "{folder_album}" '
|
||||
'--keyword-template "{created.year}". '
|
||||
"See '--replace-keywords' and Templating System below.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--replace-keywords",
|
||||
is_flag=True,
|
||||
help="Replace keywords with any values specified with --keyword-template. "
|
||||
"By default, --keyword-template will add keywords to any keywords already associated "
|
||||
"with the photo. If --replace-keywords is specified, values from --keyword-template "
|
||||
"will replace any existing keywords instead of adding additional keywords.",
|
||||
)
|
||||
@click.option(
|
||||
"--description-template",
|
||||
metavar="TEMPLATE",
|
||||
multiple=False,
|
||||
default=None,
|
||||
help="For use with --exiftool, --sidecar; specify a template string to use as "
|
||||
"description in the form '{name,DEFAULT}' "
|
||||
"This is the same format as --directory. For example, if you wanted to append "
|
||||
"'exported with osxphotos on [today's date]' to the description, you could specify "
|
||||
'--description-template "{descr} exported with osxphotos on {today.date}" '
|
||||
"See Templating System below.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--exportdb",
|
||||
help="Optional path to export database (if not in the default location in the export directory).",
|
||||
type=ExportDBType(),
|
||||
)
|
||||
@click.option(
|
||||
"--report",
|
||||
metavar="REPORT_FILE",
|
||||
help="Write a report of all files that were exported. "
|
||||
"The extension of the report filename will be used to determine the format. "
|
||||
"Valid extensions are: "
|
||||
".csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). "
|
||||
"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.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--append",
|
||||
is_flag=True,
|
||||
help="If used with --report, add data to existing report file instead of overwriting it. "
|
||||
"See also --report.",
|
||||
)
|
||||
@click.option("--verbose", "-V", is_flag=True, help="Print verbose output.")
|
||||
@click.option("--timestamp", is_flag=True, help="Add time stamp to verbose output")
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
is_flag=True,
|
||||
help="Run in dry-run mode (don't actually update files), e.g. for use with --update-signatures.",
|
||||
)
|
||||
@THEME_OPTION
|
||||
@DB_OPTION
|
||||
@click.argument(
|
||||
"export_dir",
|
||||
metavar="EXPORT_DIRECTORY",
|
||||
nargs=1,
|
||||
type=click.Path(exists=True, file_okay=False),
|
||||
)
|
||||
def exiftool(
|
||||
album_keyword,
|
||||
append,
|
||||
db_config,
|
||||
db,
|
||||
description_template,
|
||||
dry_run,
|
||||
exiftool_merge_keywords,
|
||||
exiftool_merge_persons,
|
||||
exiftool_option,
|
||||
exiftool_path,
|
||||
export_dir,
|
||||
exportdb,
|
||||
ignore_date_modified,
|
||||
keyword_template,
|
||||
load_config,
|
||||
person_keyword,
|
||||
replace_keywords,
|
||||
report,
|
||||
save_config,
|
||||
theme,
|
||||
timestamp,
|
||||
verbose,
|
||||
):
|
||||
"""Run exiftool on previously exported files to update metadata.
|
||||
|
||||
If you previously exported photos with `osxphotos export` but did not include the
|
||||
`--exiftool` option and you now want to update the metadata of the exported files with
|
||||
exiftool, you can use this command to do so.
|
||||
|
||||
If you simply re-run the `osxphotos export` with `--update` and `--exiftool`, osxphotos will
|
||||
re-export all photos because it will detect that the previously exported photos do not have the
|
||||
exiftool metadata updates. This command will run exiftool on the previously exported photos
|
||||
to update all metadata then will update the export database so that using `--exiftool --update`
|
||||
with `osxphotos export` in the future will work correctly and not unnecessarily re-export photos.
|
||||
"""
|
||||
# save locals for initializing config options
|
||||
locals_ = locals()
|
||||
|
||||
if load_config and db_config:
|
||||
raise click.UsageError("Cannot specify both --load-config and --db-config")
|
||||
|
||||
exportdb = exportdb or pathlib.Path(export_dir) / OSXPHOTOS_EXPORT_DB
|
||||
if not exportdb.exists():
|
||||
raise click.UsageError(f"Export database {exportdb} does not exist")
|
||||
|
||||
# grab all the variables we need from the export command
|
||||
# export is a click Command so can walk through it's params to get the option names
|
||||
for param in export.params:
|
||||
if param.name not in locals_:
|
||||
locals_[param.name] = None
|
||||
|
||||
# need to ensure --exiftool is true in the config options
|
||||
locals_["exiftool"] = True
|
||||
config = ConfigOptions(
|
||||
"export",
|
||||
locals_,
|
||||
ignore=[
|
||||
"cli_obj",
|
||||
"config_only",
|
||||
"ctx",
|
||||
"db_config",
|
||||
"dest",
|
||||
"export_dir",
|
||||
"load_config",
|
||||
"save_config",
|
||||
],
|
||||
)
|
||||
color_theme = get_theme(theme)
|
||||
verbose_ = verbose_print(
|
||||
verbose, timestamp, rich=True, theme=color_theme, highlight=False
|
||||
)
|
||||
# set console for rich_echo to be same as for verbose_
|
||||
set_rich_console(get_verbose_console())
|
||||
set_rich_theme(color_theme)
|
||||
set_rich_timestamp(timestamp)
|
||||
|
||||
# load config options from either file or export database
|
||||
# values already set in config will take precedence over any values
|
||||
# in the config file or database
|
||||
if load_config:
|
||||
try:
|
||||
config.load_from_file(load_config)
|
||||
except ConfigOptionsLoadError as e:
|
||||
rich_click_echo(
|
||||
f"[error]Error parsing {load_config} config file: {e.message}", err=True
|
||||
)
|
||||
sys.exit(1)
|
||||
verbose_(f"Loaded options from file [filepath]{load_config}")
|
||||
elif db_config:
|
||||
config = export_db_get_config(exportdb, config)
|
||||
verbose_("Loaded options from export database")
|
||||
|
||||
# from here on out, use config.param_name instead of using the params passed into the function
|
||||
# as the values may have been updated from config file or database
|
||||
if load_config or db_config:
|
||||
# config file might have changed verbose
|
||||
color_theme = get_theme(config.theme)
|
||||
verbose_ = verbose_print(
|
||||
config.verbose,
|
||||
config.timestamp,
|
||||
rich=True,
|
||||
theme=color_theme,
|
||||
highlight=False,
|
||||
)
|
||||
# set console for rich_echo to be same as for verbose_
|
||||
set_rich_console(get_verbose_console())
|
||||
set_rich_timestamp(config.timestamp)
|
||||
|
||||
# validate options
|
||||
if append and not report:
|
||||
raise click.UsageError("--append requires --report")
|
||||
|
||||
# need to ensure we have a photos database
|
||||
config.db = get_photos_db(config.db)
|
||||
|
||||
if save_config:
|
||||
verbose_(f"Saving options to config file '[filepath]{save_config}'")
|
||||
config.write_to_file(save_config)
|
||||
|
||||
process_files(exportdb, export_dir, verbose=verbose_, options=config)
|
||||
|
||||
|
||||
def process_files(
|
||||
exportdb: str, export_dir: str, verbose: Callable, options: ConfigOptions
|
||||
):
|
||||
"""Process files in the export database.
|
||||
|
||||
Args:
|
||||
exportdb: Path to export database.
|
||||
export_dir: Path to export directory.
|
||||
verbose: Callable for verbose output.
|
||||
options: ConfigOptions
|
||||
"""
|
||||
|
||||
if options.report:
|
||||
report = render_and_validate_report(
|
||||
options.report, options.exiftool_path, export_dir
|
||||
)
|
||||
report_writer = report_writer_factory(report, options.append)
|
||||
else:
|
||||
report_writer = ReportWriterNoOp()
|
||||
|
||||
photosdb = PhotosDB(options.db, verbose=verbose)
|
||||
if options.dry_run:
|
||||
export_db = ExportDBInMemory(exportdb, export_dir)
|
||||
fileutil = FileUtilNoOp
|
||||
else:
|
||||
export_db = ExportDB(exportdb, export_dir)
|
||||
fileutil = FileUtil
|
||||
|
||||
# get_exported_files is a generator which returns tuple of (uuid, filepath)
|
||||
files = list(export_db.get_exported_files())
|
||||
# filter out sidecar files
|
||||
files = [
|
||||
(u, f)
|
||||
for u, f in files
|
||||
if pathlib.Path(f).suffix.lower() not in [".json", ".xmp"]
|
||||
]
|
||||
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:
|
||||
if not pathlib.Path(file).exists():
|
||||
verbose(f"Skipping missing file [filepath]{file}[/]")
|
||||
report_writer.write(ExportResults(missing=[file]))
|
||||
continue
|
||||
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(
|
||||
description_template=options.description_template,
|
||||
dry_run=options.dry_run,
|
||||
exiftool_flags=options.exiftool_option,
|
||||
exiftool=True,
|
||||
export_db=export_db,
|
||||
ignore_date_modified=options.ignore_date_modified,
|
||||
keyword_template=options.keyword_template,
|
||||
merge_exif_keywords=options.exiftool_merge_keywords,
|
||||
merge_exif_persons=options.exiftool_merge_persons,
|
||||
replace_keywords=options.replace_keywords,
|
||||
use_albums_as_keywords=options.album_keyword,
|
||||
use_persons_as_keywords=options.person_keyword,
|
||||
verbose=verbose,
|
||||
)
|
||||
exporter = PhotoExporter(photo)
|
||||
results = exporter.write_exiftool_metadata_to_file(
|
||||
src=file, dest=file, options=export_options
|
||||
)
|
||||
all_results += results
|
||||
|
||||
for warning_ in results.exiftool_warning:
|
||||
verbose(
|
||||
f"[warning]exiftool warning for file {warning_[0]}: {warning_[1]}"
|
||||
)
|
||||
for error_ in results.exiftool_error:
|
||||
rich_echo_error(
|
||||
f"[error]exiftool error for file {error_[0]}: {error_[1]}"
|
||||
)
|
||||
for result in results.exif_updated:
|
||||
verbose(f"Updated EXIF metadata for [filepath]{result}")
|
||||
|
||||
# update the database
|
||||
with export_db.get_file_record(file) as rec:
|
||||
rec.dest_sig = fileutil.file_sig(file)
|
||||
rec.export_options = export_options.bit_flags
|
||||
rec.exifdata = exporter.exiftool_json_sidecar(export_options)
|
||||
|
||||
report_writer.write(results)
|
||||
count += 1
|
||||
progress.advance(task)
|
||||
|
||||
photo_str_total = pluralize(total, "photo", "photos")
|
||||
summary = (
|
||||
f"Processed: [num]{total}[/] {photo_str_total}, "
|
||||
f"skipped: [num]{len(all_results.skipped)}[/], "
|
||||
f"updated EXIF data: [num]{len(all_results.exif_updated)}[/], "
|
||||
)
|
||||
verbose(summary)
|
||||
|
||||
if options.report:
|
||||
verbose(f"Wrote export report to [filepath]{report}")
|
||||
report_writer.close()
|
||||
@@ -2729,14 +2729,3 @@ def render_and_validate_report(report: str, exiftool_path: str, export_dir: str)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
# def _export_with_profiler(args: Dict):
|
||||
# """ "Run export with cProfile"""
|
||||
# try:
|
||||
# args.pop("profile")
|
||||
# except KeyError:
|
||||
# pass
|
||||
|
||||
# cProfile.runctx(
|
||||
# "_export(**args)", globals=globals(), locals=locals(), sort="tottime"
|
||||
# )
|
||||
|
||||
@@ -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
|
||||
|
||||
474
osxphotos/cli/photo_inspect.py
Normal file
474
osxphotos/cli/photo_inspect.py
Normal file
@@ -0,0 +1,474 @@
|
||||
"""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([uuid, 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.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)
|
||||
@@ -57,8 +57,8 @@ class ConfigOptions:
|
||||
setattr(self, attr, self._attrs[attr])
|
||||
else:
|
||||
setattr(self, attr, arg)
|
||||
except KeyError:
|
||||
raise KeyError(f"Missing argument: {attr}")
|
||||
except KeyError as e:
|
||||
raise KeyError(f"Missing argument: {attr}") from e
|
||||
|
||||
def validate(self, exclusive=None, inclusive=None, dependent=None, cli=False):
|
||||
"""validate combinations of otions
|
||||
@@ -153,9 +153,26 @@ class ConfigOptions:
|
||||
ConfigOptionsLoadError if there are any errors during the parsing of the TOML file
|
||||
"""
|
||||
loaded = toml.load(filename)
|
||||
return self._load_from_toml_dict(loaded, override)
|
||||
|
||||
def load_from_str(self, str, override=False):
|
||||
"""Load options from a TOML str.
|
||||
|
||||
Args:
|
||||
str: TOML str
|
||||
override: bool; if True, values in the TOML str will override values already set in the instance
|
||||
|
||||
Raises:
|
||||
ConfigOptionsLoadError if there are any errors during the parsing of the TOML str
|
||||
"""
|
||||
loaded = toml.loads(str)
|
||||
return self._load_from_toml_dict(loaded, override)
|
||||
|
||||
def _load_from_toml_dict(self, loaded, override):
|
||||
"""Load options from a TOML dict (as returned by toml.load or toml.loads)"""
|
||||
name = self._name
|
||||
if name not in loaded:
|
||||
raise ConfigOptionsLoadError(f"[{name}] section missing from {filename}")
|
||||
raise ConfigOptionsLoadError(f"[{name}] section missing from config file")
|
||||
|
||||
for attr in loaded[name]:
|
||||
if attr not in self._attrs:
|
||||
|
||||
Binary file not shown.
@@ -15,7 +15,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, Optional, Tuple, Union, List
|
||||
|
||||
from tenacity import retry, stop_after_attempt
|
||||
|
||||
@@ -30,7 +30,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 +171,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
|
||||
@@ -282,6 +293,20 @@ class ExportDB:
|
||||
results = None
|
||||
return results
|
||||
|
||||
def get_exported_files(self):
|
||||
"""Returns tuple of (uuid, filepath) for all paths of all exported files tracked in the database"""
|
||||
conn = self._conn
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT uuid, filepath FROM export_data")
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
return
|
||||
|
||||
while row := c.fetchone():
|
||||
yield row[0], os.path.join(self.export_dir, row[1])
|
||||
return
|
||||
|
||||
def close(self):
|
||||
"""close the database connection"""
|
||||
try:
|
||||
@@ -299,7 +324,7 @@ class ExportDB:
|
||||
if not os.path.isfile(dbfile):
|
||||
conn = self._get_db_connection(dbfile)
|
||||
if not conn:
|
||||
raise Exception("Error getting connection to database {dbfile}")
|
||||
raise Exception(f"Error getting connection to database {dbfile}")
|
||||
self._create_or_migrate_db_tables(conn)
|
||||
self.was_created = True
|
||||
self.was_upgraded = ()
|
||||
@@ -456,6 +481,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()
|
||||
|
||||
@@ -673,6 +702,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:
|
||||
@@ -1034,6 +1089,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
|
||||
@@ -1042,6 +1112,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,
|
||||
|
||||
@@ -12,10 +12,11 @@ from rich import print
|
||||
|
||||
from ._constants import OSXPHOTOS_EXPORT_DB
|
||||
from ._version import __version__
|
||||
from .utils import noop
|
||||
from .configoptions import ConfigOptions
|
||||
from .export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDB
|
||||
from .fileutil import FileUtil
|
||||
from .photosdb import PhotosDB
|
||||
from .utils import noop
|
||||
|
||||
__all__ = [
|
||||
"export_db_check_signatures",
|
||||
@@ -125,6 +126,23 @@ def export_db_save_config_to_file(
|
||||
f.write(row[0])
|
||||
|
||||
|
||||
def export_db_get_config(
|
||||
export_db: Union[str, pathlib.Path], config: ConfigOptions, override=False
|
||||
) -> ConfigOptions:
|
||||
"""Load last run config to config
|
||||
|
||||
Args:
|
||||
export_db: path to export database
|
||||
override: if True, any loaded config values will overwrite existing values in config
|
||||
"""
|
||||
conn = sqlite3.connect(str(export_db))
|
||||
c = conn.cursor()
|
||||
row = c.execute("SELECT config FROM config ORDER BY id DESC LIMIT 1;").fetchone()
|
||||
if not row:
|
||||
return ValueError("No config found in export_db")
|
||||
return config.load_from_str(row[0], override=override)
|
||||
|
||||
|
||||
def export_db_check_signatures(
|
||||
dbfile: Union[str, pathlib.Path],
|
||||
export_dir: Union[str, pathlib.Path],
|
||||
|
||||
@@ -735,7 +735,7 @@ class PhotoExporter:
|
||||
return ShouldUpdate.EXPORT_OPTIONS_DIFFERENT
|
||||
|
||||
if options.exiftool:
|
||||
current_exifdata = self._exiftool_json_sidecar(options=options)
|
||||
current_exifdata = self.exiftool_json_sidecar(options=options)
|
||||
rv = current_exifdata != file_record.exifdata
|
||||
# if using exiftool, don't need to continue checking edited below
|
||||
# as exiftool will be used to update edited file
|
||||
@@ -1143,7 +1143,7 @@ class PhotoExporter:
|
||||
# point src to the tmp_file so that the original source is not modified
|
||||
# and the export grabs the new file
|
||||
src = tmp_file
|
||||
exif_results = self._write_exif_metadata_to_file(
|
||||
exif_results = self.write_exiftool_metadata_to_file(
|
||||
src, dest, options=options
|
||||
)
|
||||
|
||||
@@ -1185,7 +1185,7 @@ class PhotoExporter:
|
||||
if not options.ignore_signature:
|
||||
rec.dest_sig = fileutil.file_sig(dest)
|
||||
if options.exiftool:
|
||||
rec.exifdata = self._exiftool_json_sidecar(options)
|
||||
rec.exifdata = self.exiftool_json_sidecar(options)
|
||||
if self.photo.hexdigest != rec.digest:
|
||||
results.metadata_changed = [dest_str]
|
||||
rec.digest = self.photo.hexdigest
|
||||
@@ -1320,7 +1320,7 @@ class PhotoExporter:
|
||||
sidecar_filename = dest.parent / pathlib.Path(
|
||||
f"{dest.stem}{dest_suffix}.json"
|
||||
)
|
||||
sidecar_str = self._exiftool_json_sidecar(
|
||||
sidecar_str = self.exiftool_json_sidecar(
|
||||
filename=dest.name, options=options
|
||||
)
|
||||
sidecars.append(
|
||||
@@ -1337,7 +1337,7 @@ class PhotoExporter:
|
||||
sidecar_filename = dest.parent / pathlib.Path(
|
||||
f"{dest.stem}{dest_suffix}.json"
|
||||
)
|
||||
sidecar_str = self._exiftool_json_sidecar(
|
||||
sidecar_str = self.exiftool_json_sidecar(
|
||||
tag_groups=False, filename=dest.name, options=options
|
||||
)
|
||||
sidecars.append(
|
||||
@@ -1436,19 +1436,20 @@ class PhotoExporter:
|
||||
|
||||
return results
|
||||
|
||||
def _write_exif_metadata_to_file(
|
||||
def write_exiftool_metadata_to_file(
|
||||
self,
|
||||
src,
|
||||
dest,
|
||||
options: ExportOptions,
|
||||
) -> ExportResults:
|
||||
"""Write exif metadata to file using exiftool
|
||||
"""Write exif metadata to src file using exiftool
|
||||
|
||||
Note: this method modifies src so src must be a copy of the original file;
|
||||
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
|
||||
local machine prior to being copied to the export destination which may be on a
|
||||
network drive or other slower external storage."""
|
||||
network drive or other slower external storage)."""
|
||||
|
||||
verbose = options.verbose or self._verbose
|
||||
exiftool_results = ExportResults()
|
||||
@@ -1491,7 +1492,7 @@ class PhotoExporter:
|
||||
old_data = exif_record.exifdata if exif_record else None
|
||||
if old_data is not None:
|
||||
old_data = json.loads(old_data)[0]
|
||||
current_data = json.loads(self._exiftool_json_sidecar(options=options))
|
||||
current_data = json.loads(self.exiftool_json_sidecar(options=options))
|
||||
current_data = current_data[0]
|
||||
if old_data != current_data:
|
||||
files_are_different = True
|
||||
@@ -1831,7 +1832,7 @@ class PhotoExporter:
|
||||
pass
|
||||
return persons
|
||||
|
||||
def _exiftool_json_sidecar(
|
||||
def exiftool_json_sidecar(
|
||||
self,
|
||||
options: t.Optional[ExportOptions] = None,
|
||||
tag_groups: bool = True,
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -80,19 +80,19 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# generate JSON files
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json")
|
||||
json_ = exporter._exiftool_json_sidecar()
|
||||
json_ = exporter.exiftool_json_sidecar()
|
||||
with open(sidecar, "w") as file:
|
||||
file.write(json_)
|
||||
|
||||
# no tag groups
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_no_tag_groups.json")
|
||||
json_ = exporter._exiftool_json_sidecar(tag_groups=False)
|
||||
json_ = exporter.exiftool_json_sidecar(tag_groups=False)
|
||||
with open(sidecar, "w") as file:
|
||||
file.write(json_)
|
||||
|
||||
# ignore_date_modified
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_ignore_date_modified.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(ignore_date_modified=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -100,7 +100,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# keyword_template
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_keyword_template.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=["{folder_album}"])
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -108,7 +108,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# persons_as_keywords
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_persons_as_keywords.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(use_persons_as_keywords=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -116,7 +116,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# albums_as_keywords
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_albums_as_keywords.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(use_albums_as_keywords=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
|
||||
266
tests/test_cli_exiftool.py
Normal file
266
tests/test_cli_exiftool.py
Normal file
@@ -0,0 +1,266 @@
|
||||
"""Tests for `osxphotos exiftool` command."""
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from osxphotos.cli.exiftool_cli import exiftool
|
||||
from osxphotos.cli.export import export
|
||||
from osxphotos.exiftool import ExifTool, get_exiftool_path
|
||||
|
||||
from .test_cli import CLI_EXIFTOOL, PHOTOS_DB_15_7
|
||||
|
||||
# determine if exiftool installed so exiftool tests can be skipped
|
||||
try:
|
||||
exiftool_path = get_exiftool_path()
|
||||
except FileNotFoundError:
|
||||
exiftool_path = None
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool():
|
||||
"""Test osxphotos exiftool"""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
files = glob.glob("*")
|
||||
assert sorted(files) == sorted(
|
||||
[CLI_EXIFTOOL[uuid]["File:FileName"] for uuid in CLI_EXIFTOOL]
|
||||
)
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
["--db", os.path.join(cwd, PHOTOS_DB_15_7), "-V", "--db-config", temp_dir],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict()
|
||||
for key in CLI_EXIFTOOL[uuid]:
|
||||
if type(exif[key]) == list:
|
||||
assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key])
|
||||
else:
|
||||
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_album_keyword():
|
||||
"""Test osxphotos exiftool with --album-template."""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--album",
|
||||
"Pumpkin Farm",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
files = glob.glob("*")
|
||||
assert len(files) == 3
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"-V",
|
||||
"--db-config",
|
||||
"--report",
|
||||
"exiftool.json",
|
||||
"--album-keyword",
|
||||
temp_dir,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
report = json.load(open("exiftool.json", "r"))
|
||||
assert len(report) == 3
|
||||
|
||||
# verify exiftool metadata was updated
|
||||
for file in report:
|
||||
exif = ExifTool(file["filename"]).asdict()
|
||||
assert "Pumpkin Farm" in exif["IPTC:Keywords"]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
"--album",
|
||||
"Pumpkin Farm",
|
||||
"--album-keyword",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: 3" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_keyword_template():
|
||||
"""Test osxphotos exiftool with --keyword-template."""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"-V",
|
||||
"--db-config",
|
||||
"--keyword-template",
|
||||
"FOO",
|
||||
temp_dir,
|
||||
"--report",
|
||||
"exiftool.json",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
report = json.load(open("exiftool.json", "r"))
|
||||
for file in report:
|
||||
exif = ExifTool(file["filename"]).asdict()
|
||||
assert "FOO" in exif["IPTC:Keywords"]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--keyword-template",
|
||||
"FOO",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_load_config():
|
||||
"""Test osxphotos exiftool with --load-config"""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--save-config",
|
||||
"config.toml",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
["-V", "--load-config", "config.toml", temp_dir],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict()
|
||||
for key in CLI_EXIFTOOL[uuid]:
|
||||
if type(exif[key]) == list:
|
||||
assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key])
|
||||
else:
|
||||
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
@@ -1,6 +1,8 @@
|
||||
""" test ConfigOptions class """
|
||||
|
||||
import pathlib
|
||||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
import toml
|
||||
|
||||
@@ -43,6 +45,15 @@ def test_write_to_file_load_from_file(tmpdir):
|
||||
assert cfg2.bar
|
||||
|
||||
|
||||
def test_load_from_str(tmpdir):
|
||||
cfg = ConfigOptions("test", VARS)
|
||||
cfg.bar = True
|
||||
cfg_str = cfg.write_to_str()
|
||||
cfg2 = ConfigOptions("test", VARS).load_from_str(cfg_str)
|
||||
assert cfg2.foo == "bar"
|
||||
assert cfg2.bar
|
||||
|
||||
|
||||
def test_load_from_file_error(tmpdir):
|
||||
cfg_file = pathlib.Path(str(tmpdir)) / "test.toml"
|
||||
cfg = ConfigOptions("test", VARS)
|
||||
|
||||
@@ -404,7 +404,7 @@ def test_exiftool_json_sidecar(photosdb):
|
||||
with open(str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json"), "r") as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar()
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar()
|
||||
json_got = json.loads(json_got)[0]
|
||||
|
||||
assert json_got == json_expected
|
||||
@@ -420,7 +420,7 @@ def test_exiftool_json_sidecar_ignore_date_modified(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(ignore_date_modified=True)
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -453,7 +453,7 @@ def test_exiftool_json_sidecar_keyword_template_long(capsys, photosdb):
|
||||
|
||||
long_str = "x" * (_MAX_IPTC_KEYWORD_LEN + 1)
|
||||
photos[0]._verbose = print
|
||||
json_got = PhotoExporter(photos[0])._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photos[0]).exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=[long_str])
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -479,7 +479,7 @@ def test_exiftool_json_sidecar_keyword_template(photosdb):
|
||||
str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_keyword_template.json"), "r"
|
||||
) as fp:
|
||||
json_expected = json.load(fp)
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=["{folder_album}"])
|
||||
)
|
||||
json_got = json.loads(json_got)
|
||||
@@ -497,7 +497,7 @@ def test_exiftool_json_sidecar_use_persons_keyword(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(use_persons_as_keywords=True)
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -515,7 +515,7 @@ def test_exiftool_json_sidecar_use_albums_keywords(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(use_albums_as_keywords=True)
|
||||
)
|
||||
json_got = json.loads(json_got)
|
||||
@@ -530,7 +530,7 @@ def test_exiftool_sidecar(photosdb):
|
||||
with open(pathlib.Path(SIDECAR_DIR) / f"{uuid}_no_tag_groups.json", "r") as fp:
|
||||
json_expected = fp.read()
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(tag_groups=False)
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(tag_groups=False)
|
||||
|
||||
assert json_got == json_expected
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -337,7 +337,7 @@ def test_exiftool_json_sidecar(photosdb):
|
||||
with open(str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json"), "r") as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar()
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar()
|
||||
json_got = json.loads(json_got)[0]
|
||||
|
||||
assert json_got == json_expected
|
||||
|
||||
Reference in New Issue
Block a user