Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae5b02f563 | ||
|
|
aa1a96d201 | ||
|
|
d9f24307ac | ||
|
|
958f8c343a | ||
|
|
70cf4c9f92 | ||
|
|
2d3344ee34 | ||
|
|
b4bc906b6a | ||
|
|
520a15fac6 | ||
|
|
032dff8967 | ||
|
|
3c36b0fb33 | ||
|
|
d51d7a41e4 | ||
|
|
60c926fea5 | ||
|
|
db27aac14b | ||
|
|
d17454772c | ||
|
|
9c9e73ba96 | ||
|
|
e21a78c2b3 | ||
|
|
de0fbf2bb9 | ||
|
|
b330e27fb8 | ||
|
|
a941f66d62 | ||
|
|
d77eba12b2 | ||
|
|
de94fd76de | ||
|
|
1026473684 | ||
|
|
3f9c9893c3 | ||
|
|
574cdd65a3 | ||
|
|
5b9547669e | ||
|
|
35b5bbd13d | ||
|
|
6870ad0d8e | ||
|
|
17ac5949e1 | ||
|
|
ffb9af1965 | ||
|
|
595307a003 | ||
|
|
79a50b9e50 | ||
|
|
515df0a5dc | ||
|
|
63bfa92563 | ||
|
|
44a1e3e7a7 | ||
|
|
6c84e476cc | ||
|
|
14fbe5e068 | ||
|
|
ebac9d0bfb | ||
|
|
29716c5272 | ||
|
|
fbe8229103 | ||
|
|
5ee6affc05 | ||
|
|
b3a7869bd3 | ||
|
|
e5f1c29974 | ||
|
|
70848e1ff6 | ||
|
|
4b7a53faa8 | ||
|
|
a78dd80af4 | ||
|
|
1316866dc4 | ||
|
|
30273509d4 | ||
|
|
15a3e69015 | ||
|
|
2691902d5c | ||
|
|
da47821fae | ||
|
|
6f38e2da49 | ||
|
|
857e3db6cc | ||
|
|
7ed3115f36 | ||
|
|
198addaa07 | ||
|
|
d91fc93737 | ||
|
|
5c3360f29d | ||
|
|
d4513832a6 |
@@ -175,6 +175,24 @@
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "neilpa",
|
||||
"name": "Neil Pankey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/42419?v=4",
|
||||
"profile": "https://neilpa.me",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AaronVanGeffen",
|
||||
"name": "Aaron van Geffen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/604665?v=4",
|
||||
"profile": "https://aaronweb.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
150
CHANGELOG.md
@@ -4,6 +4,156 @@ 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.41.10](https://github.com/RhetTbull/osxphotos/compare/v0.41.9...v0.41.10)
|
||||
|
||||
> 9 April 2021
|
||||
|
||||
- Added --query-eval, implements #280 [`b4bc906`](https://github.com/RhetTbull/osxphotos/commit/b4bc906b6a1c3444c5f5a5d9d908ab8c955c8f7e)
|
||||
|
||||
#### [v0.41.9](https://github.com/RhetTbull/osxphotos/compare/v0.41.8...v0.41.9)
|
||||
|
||||
> 5 April 2021
|
||||
|
||||
- Bug fix for #414, exiftool str replace [`032dff8`](https://github.com/RhetTbull/osxphotos/commit/032dff89677f049a234d9f498951b8b402d1b31c)
|
||||
|
||||
#### [v0.41.8](https://github.com/RhetTbull/osxphotos/compare/v0.41.7...v0.41.8)
|
||||
|
||||
> 4 April 2021
|
||||
|
||||
- Added --name to search filename, closes #249, #412 [`#249`](https://github.com/RhetTbull/osxphotos/issues/249)
|
||||
|
||||
#### [v0.41.7](https://github.com/RhetTbull/osxphotos/compare/v0.41.6...v0.41.7)
|
||||
|
||||
> 3 April 2021
|
||||
|
||||
- Bump pygments from 2.6.1 to 2.7.4 [`#408`](https://github.com/RhetTbull/osxphotos/pull/408)
|
||||
- Removed logging.debug code [`e21a78c`](https://github.com/RhetTbull/osxphotos/commit/e21a78c2b39ee82610394b447a9aa697e489c3e4)
|
||||
- Added test for #409 [`db27aac`](https://github.com/RhetTbull/osxphotos/commit/db27aac14bbaff0b2db44f8b2d41022ebcad18a7)
|
||||
- Update phototemplate.py [`d174547`](https://github.com/RhetTbull/osxphotos/commit/d17454772cebbd6edd5d8e0f04e80feecbdb2355)
|
||||
|
||||
#### [v0.41.6](https://github.com/RhetTbull/osxphotos/compare/v0.41.5...v0.41.6)
|
||||
|
||||
> 28 March 2021
|
||||
|
||||
- Added --retry, issue #406 [`b330e27`](https://github.com/RhetTbull/osxphotos/commit/b330e27fb838b702cefcbdb588c2fbb924b4cbc4)
|
||||
|
||||
#### [v0.41.5](https://github.com/RhetTbull/osxphotos/compare/v0.41.4...v0.41.5)
|
||||
|
||||
> 27 March 2021
|
||||
|
||||
- Bump pyyaml from 5.1.2 to 5.4 [`#402`](https://github.com/RhetTbull/osxphotos/pull/402)
|
||||
- Fixed albums for burst images, closes #401, #403, #404 [`#401`](https://github.com/RhetTbull/osxphotos/issues/401)
|
||||
|
||||
#### [v0.41.4](https://github.com/RhetTbull/osxphotos/compare/v0.41.3...v0.41.4)
|
||||
|
||||
> 22 March 2021
|
||||
|
||||
- Bump pillow from 7.2.0 to 8.1.1 [`#399`](https://github.com/RhetTbull/osxphotos/pull/399)
|
||||
- Added --from-time, --to-time, closes #400 [`#400`](https://github.com/RhetTbull/osxphotos/issues/400)
|
||||
|
||||
#### [v0.41.3](https://github.com/RhetTbull/osxphotos/compare/v0.41.2...v0.41.3)
|
||||
|
||||
> 14 March 2021
|
||||
|
||||
- docs: add AaronVanGeffen as a contributor [`#398`](https://github.com/RhetTbull/osxphotos/pull/398)
|
||||
- Use original filename to export photos by default [`#396`](https://github.com/RhetTbull/osxphotos/pull/396)
|
||||
- Updated docs for --cleanup, #394 [`17ac594`](https://github.com/RhetTbull/osxphotos/commit/17ac5949e15057379eb13b979d4d7498bbb94d67)
|
||||
- Add --cleanup files to report, #395 [`5b95476`](https://github.com/RhetTbull/osxphotos/commit/5b9547669ed6622ae06607e024315e383c0b2d98)
|
||||
|
||||
#### [v0.41.2](https://github.com/RhetTbull/osxphotos/compare/v0.41.1...v0.41.2)
|
||||
|
||||
> 14 March 2021
|
||||
|
||||
- Fix for long descriptions with exiftool, #393 [`ffb9af1`](https://github.com/RhetTbull/osxphotos/commit/ffb9af1965668bcfc2422f08b2462964a7dae3e2)
|
||||
|
||||
#### [v0.41.1](https://github.com/RhetTbull/osxphotos/compare/v0.41.0...v0.41.1)
|
||||
|
||||
> 5 March 2021
|
||||
|
||||
- Bug fix, convert PosixPath to str, #392 [`595307a`](https://github.com/RhetTbull/osxphotos/commit/595307a003c8ae5d3bee3ad161bb880d884b3cc3)
|
||||
|
||||
#### [v0.41.0](https://github.com/RhetTbull/osxphotos/compare/v0.40.19...v0.41.0)
|
||||
|
||||
> 22 February 2021
|
||||
|
||||
- Template refactor [`#385`](https://github.com/RhetTbull/osxphotos/pull/385)
|
||||
|
||||
#### [v0.40.19](https://github.com/RhetTbull/osxphotos/compare/v0.40.18...v0.40.19)
|
||||
|
||||
> 20 February 2021
|
||||
|
||||
- Better exception handling for AdjustmentsInfo [`44a1e3e`](https://github.com/RhetTbull/osxphotos/commit/44a1e3e7a7f765bf91c2341e423ec9e5a9e3c1bd)
|
||||
|
||||
#### [v0.40.18](https://github.com/RhetTbull/osxphotos/compare/v0.40.17...v0.40.18)
|
||||
|
||||
> 20 February 2021
|
||||
|
||||
- docs: add neilpa as a contributor [`#383`](https://github.com/RhetTbull/osxphotos/pull/383)
|
||||
- Added AdjustmentsInfo, #150, #379 [`5ee6aff`](https://github.com/RhetTbull/osxphotos/commit/5ee6affc0525db1975cb5095f62494ef10d92f7e)
|
||||
- docs: update .all-contributorsrc [skip ci] [`ebac9d0`](https://github.com/RhetTbull/osxphotos/commit/ebac9d0bfb43f59f046aacdd0290d1fcd29a3b5e)
|
||||
- docs: update README.md [skip ci] [`29716c5`](https://github.com/RhetTbull/osxphotos/commit/29716c52726a4e699c03d43ecc67db57f55b36f8)
|
||||
- Version bump [`fbe8229`](https://github.com/RhetTbull/osxphotos/commit/fbe822910370652975ab83b82344169df4c3027c)
|
||||
|
||||
#### [v0.40.17](https://github.com/RhetTbull/osxphotos/compare/v0.40.16...v0.40.17)
|
||||
|
||||
> 18 February 2021
|
||||
|
||||
- Updated docs for --ignore-signature, #286 [`e5f1c29`](https://github.com/RhetTbull/osxphotos/commit/e5f1c299742fcfa0a855a33df7b266aa2c39e48b)
|
||||
- Added depth_state to _info [`b3a7869`](https://github.com/RhetTbull/osxphotos/commit/b3a7869bd3cc13e40cb3f68ff8caf12edda9a49c)
|
||||
|
||||
#### [v0.40.16](https://github.com/RhetTbull/osxphotos/compare/v0.40.14...v0.40.16)
|
||||
|
||||
> 14 February 2021
|
||||
|
||||
- Write description to ITPC:CaptionAbstract (#380) [`4b7a53f`](https://github.com/RhetTbull/osxphotos/commit/4b7a53faa8d7ff2e941e7653554f61bcbd416fc9)
|
||||
- Removed orientation from XMP, #378 [`70848e1`](https://github.com/RhetTbull/osxphotos/commit/70848e1ff6def928b052271b47c1697c23a8c73f)
|
||||
- Added image orientation bug to Known Bugs [`1316866`](https://github.com/RhetTbull/osxphotos/commit/1316866dc47486ac61db8903d2d7d006f2598a77)
|
||||
|
||||
#### [v0.40.14](https://github.com/RhetTbull/osxphotos/compare/v0.40.13...v0.40.14)
|
||||
|
||||
> 12 February 2021
|
||||
|
||||
- Fix for issue #366, --jpeg-ext, --convert-to-jpeg bug [`3027350`](https://github.com/RhetTbull/osxphotos/commit/30273509d40a270d2610b662ed9238449350064c)
|
||||
- Added test for #374 [`2691902`](https://github.com/RhetTbull/osxphotos/commit/2691902d5c7a4f4f81e3a9b36fd560ff0a07aec1)
|
||||
|
||||
#### [v0.40.13](https://github.com/RhetTbull/osxphotos/compare/v0.40.12...v0.40.13)
|
||||
|
||||
> 10 February 2021
|
||||
|
||||
- Bug fix for --jpeg-ext, #374 [`da47821`](https://github.com/RhetTbull/osxphotos/commit/da47821fae7ee7b2d6d89f5542e729e01d3338df)
|
||||
|
||||
#### [v0.40.12](https://github.com/RhetTbull/osxphotos/compare/v0.40.11...v0.40.12)
|
||||
|
||||
> 9 February 2021
|
||||
|
||||
- Fixed --exiftool-option, #369, for real this time [`857e3db`](https://github.com/RhetTbull/osxphotos/commit/857e3db6ccce810d682cd4632ac9bc8448c4f86b)
|
||||
|
||||
#### [v0.40.11](https://github.com/RhetTbull/osxphotos/compare/v0.40.10...v0.40.11)
|
||||
|
||||
> 9 February 2021
|
||||
|
||||
- Fixed --exiftool-option, #369 [`198adda`](https://github.com/RhetTbull/osxphotos/commit/198addaa07a86ac5b0fd82787fdffff0a0fc19c6)
|
||||
|
||||
#### [v0.40.10](https://github.com/RhetTbull/osxphotos/compare/v0.40.9...v0.40.10)
|
||||
|
||||
> 7 February 2021
|
||||
|
||||
- Fix for issue #366 [`5c3360f`](https://github.com/RhetTbull/osxphotos/commit/5c3360f29d52df2f804c70f37a2ca9a3f102d93c)
|
||||
|
||||
#### [v0.40.9](https://github.com/RhetTbull/osxphotos/compare/v0.40.8...v0.40.9)
|
||||
|
||||
> 7 February 2021
|
||||
|
||||
- Fixed unnecessary warning for long keywords, issue #365 [`f8616ac`](https://github.com/RhetTbull/osxphotos/commit/f8616acf167b5e73ab3e4b68dcfbf578230c330d)
|
||||
|
||||
#### [v0.40.8](https://github.com/RhetTbull/osxphotos/compare/v0.40.7...v0.40.8)
|
||||
|
||||
> 4 February 2021
|
||||
|
||||
- Implemented --in-album, --not-in-album, issue #364 [`addd952`](https://github.com/RhetTbull/osxphotos/commit/addd952aa315007852945a352b2c7c451ba5f21a)
|
||||
- Updated docs [`7fa5fba`](https://github.com/RhetTbull/osxphotos/commit/7fa5fbaa5b7c9aa1412eceef56e068dc044c91e0)
|
||||
- Updated docs Makefile [skip ci] [`683dfe7`](https://github.com/RhetTbull/osxphotos/commit/683dfe7f3ffd235659b58f403562ce2d51123cfb)
|
||||
|
||||
#### [v0.40.7](https://github.com/RhetTbull/osxphotos/compare/v0.40.6...v0.40.7)
|
||||
|
||||
> 3 February 2021
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
include README.md
|
||||
include osxphotos/templates/*
|
||||
include README.rst
|
||||
include osxphotos/templates/*
|
||||
include osxphotos/phototemplate.tx
|
||||
include osxphotos/phototemplate.md
|
||||
@@ -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: 17bac89a05164f8fa737ad20fcd95f80
|
||||
config: 0179796ca3088aa7a0ec190387bfe684
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Overview: module code — osxphotos 0.40.8 documentation</title>
|
||||
<title>Overview: module code — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||||
@@ -93,7 +93,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photoinfo._photoinfo_exifinfo — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photoinfo._photoinfo_exifinfo — osxphotos 0.41.4 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -183,7 +183,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photoinfo._photoinfo_export — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photoinfo._photoinfo_export — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -120,6 +120,8 @@
|
||||
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">deleted_files</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">deleted_directories</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span> <span class="o">=</span> <span class="n">exported</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">new</span> <span class="o">=</span> <span class="n">new</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
@@ -140,6 +142,8 @@
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">=</span> <span class="n">exiftool_error</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_written</span> <span class="o">=</span> <span class="n">xattr_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_skipped</span> <span class="o">=</span> <span class="n">xattr_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">=</span> <span class="n">deleted_files</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span> <span class="o">=</span> <span class="n">deleted_directories</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">all_files</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" return all filenames contained in results """</span>
|
||||
@@ -184,6 +188,8 @@
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">error</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exiftool_warning</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">exiftool_error</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">deleted_files</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">deleted_directories</span>
|
||||
<span class="k">return</span> <span class="bp">self</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
@@ -206,6 +212,8 @@
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",error=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exiftool_warning=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exiftool_error=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",deleted_files=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",deleted_directories=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="s2">")"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
@@ -508,6 +516,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">jpeg_ext</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""export photo, like export but with update and dry_run options</span>
|
||||
<span class="sd"> dest: must be valid destination path or exception raised</span>
|
||||
@@ -560,6 +571,9 @@
|
||||
<span class="sd"> merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> jpeg_ext: if set, will use this value for extension on jpegs converted to jpeg with convert_to_jpeg; if not set, uses jpeg; do not include the leading "."</span>
|
||||
<span class="sd"> persons: if True, include persons in exported metadata</span>
|
||||
<span class="sd"> location: if True, include location in exported metadata</span>
|
||||
<span class="sd"> replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
||||
|
||||
<span class="sd"> Returns: ExportResults class </span>
|
||||
<span class="sd"> ExportResults has attributes: </span>
|
||||
@@ -595,11 +609,11 @@
|
||||
<span class="k">if</span> <span class="n">export_db</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">export_db</span> <span class="o">=</span> <span class="n">ExportDBNoOp</span><span class="p">()</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">verbose</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">verbose</span> <span class="o">=</span> <span class="n">noop</span>
|
||||
<span class="k">elif</span> <span class="ow">not</span> <span class="n">callable</span><span class="p">(</span><span class="n">verbose</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="n">verbose</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">callable</span><span class="p">(</span><span class="n">verbose</span><span class="p">):</span>
|
||||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"verbose must be callable"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span> <span class="o">=</span> <span class="n">verbose</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">verbose</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">verbose</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
||||
|
||||
<span class="c1"># suffix to add to edited files</span>
|
||||
<span class="c1"># e.g. name will be filename_edited.jpg</span>
|
||||
@@ -640,9 +654,9 @@
|
||||
<span class="p">)</span>
|
||||
<span class="n">edited_name</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">path_edited</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
|
||||
<span class="n">edited_suffix</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">edited_name</span><span class="p">)</span><span class="o">.</span><span class="n">suffix</span>
|
||||
<span class="n">fname</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span> <span class="o">+</span> <span class="n">edited_identifier</span> <span class="o">+</span> <span class="n">edited_suffix</span>
|
||||
<span class="n">fname</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span> <span class="o">+</span> <span class="n">edited_identifier</span> <span class="o">+</span> <span class="n">edited_suffix</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">fname</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span>
|
||||
<span class="n">fname</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">original_filename</span>
|
||||
|
||||
<span class="n">uti</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti</span> <span class="k">if</span> <span class="n">edited</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">uti_original</span>
|
||||
<span class="k">if</span> <span class="n">convert_to_jpeg</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span> <span class="ow">and</span> <span class="n">uti</span> <span class="o">!=</span> <span class="s2">"public.jpeg"</span><span class="p">:</span>
|
||||
@@ -974,6 +988,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</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">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="p">(</span>
|
||||
@@ -997,6 +1014,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</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">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="p">(</span>
|
||||
@@ -1016,6 +1036,9 @@
|
||||
<span class="n">keyword_template</span><span class="o">=</span><span class="n">keyword_template</span><span class="p">,</span>
|
||||
<span class="n">description_template</span><span class="o">=</span><span class="n">description_template</span><span class="p">,</span>
|
||||
<span class="n">extension</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">suffix</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">if</span> <span class="n">dest</span><span class="o">.</span><span class="n">suffix</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="p">(</span>
|
||||
@@ -1083,6 +1106,9 @@
|
||||
<span class="n">ignore_date_modified</span><span class="o">=</span><span class="n">ignore_date_modified</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</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>
|
||||
@@ -1103,6 +1129,9 @@
|
||||
<span class="n">flags</span><span class="o">=</span><span class="n">exiftool_flags</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">warning_</span><span class="p">:</span>
|
||||
<span class="n">all_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">exported_file</span><span class="p">,</span> <span class="n">warning_</span><span class="p">))</span>
|
||||
@@ -1120,6 +1149,9 @@
|
||||
<span class="n">ignore_date_modified</span><span class="o">=</span><span class="n">ignore_date_modified</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">export_db</span><span class="o">.</span><span class="n">set_stat_exif_for_file</span><span class="p">(</span>
|
||||
@@ -1142,6 +1174,9 @@
|
||||
<span class="n">flags</span><span class="o">=</span><span class="n">exiftool_flags</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">warning_</span><span class="p">:</span>
|
||||
<span class="n">all_results</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">exported_file</span><span class="p">,</span> <span class="n">warning_</span><span class="p">))</span>
|
||||
@@ -1159,6 +1194,9 @@
|
||||
<span class="n">ignore_date_modified</span><span class="o">=</span><span class="n">ignore_date_modified</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">export_db</span><span class="o">.</span><span class="n">set_stat_exif_for_file</span><span class="p">(</span>
|
||||
@@ -1378,6 +1416,9 @@
|
||||
<span class="n">flags</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""write exif data to image file at filepath</span>
|
||||
|
||||
@@ -1388,6 +1429,9 @@
|
||||
<span class="sd"> keyword_template: (list of strings); list of template strings to render as keywords</span>
|
||||
<span class="sd"> ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set</span>
|
||||
<span class="sd"> flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F)</span>
|
||||
<span class="sd"> persons: if True, write person data to metadata</span>
|
||||
<span class="sd"> location: if True, write location data to metadata</span>
|
||||
<span class="sd"> replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> (warning, error) of warning and error strings if exiftool produces warnings or errors</span>
|
||||
@@ -1402,6 +1446,9 @@
|
||||
<span class="n">ignore_date_modified</span><span class="o">=</span><span class="n">ignore_date_modified</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">with</span> <span class="n">ExifTool</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">flags</span><span class="o">=</span><span class="n">flags</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_exiftool_path</span><span class="p">)</span> <span class="k">as</span> <span class="n">exiftool</span><span class="p">:</span>
|
||||
@@ -1424,6 +1471,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</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="kc">None</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.</span>
|
||||
<span class="sd"> Does not include all the EXIF fields as those are likely already in the image.</span>
|
||||
@@ -1437,6 +1487,9 @@
|
||||
<span class="sd"> ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set</span>
|
||||
<span class="sd"> merge_exif_keywords: merge keywords in the file's exif metadata (requires exiftool)</span>
|
||||
<span class="sd"> merge_exif_persons: merge persons in the file's exif metadata (requires exiftool)</span>
|
||||
<span class="sd"> persons: if True, include person data</span>
|
||||
<span class="sd"> location: if True, include location data</span>
|
||||
<span class="sd"> replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
||||
|
||||
<span class="sd"> Returns: dict with exiftool tags / values</span>
|
||||
|
||||
@@ -1444,8 +1497,10 @@
|
||||
<span class="sd"> EXIF:ImageDescription (may include template)</span>
|
||||
<span class="sd"> XMP:Description (may include template)</span>
|
||||
<span class="sd"> XMP:Title</span>
|
||||
<span class="sd"> IPTC:ObjectName</span>
|
||||
<span class="sd"> XMP:TagsList (may include album name, person name, or template)</span>
|
||||
<span class="sd"> IPTC:Keywords (may include album name, person name, or template)</span>
|
||||
<span class="sd"> IPTC:Caption-Abstract</span>
|
||||
<span class="sd"> XMP:Subject (set to keywords + persons)</span>
|
||||
<span class="sd"> XMP:PersonInImage</span>
|
||||
<span class="sd"> EXIF:GPSLatitudeRef, EXIF:GPSLongitudeRef</span>
|
||||
@@ -1461,6 +1516,9 @@
|
||||
<span class="sd"> QuickTime:ModifyDate (UTC)</span>
|
||||
<span class="sd"> QuickTime:GPSCoordinates</span>
|
||||
<span class="sd"> UserData:GPSCoordinates</span>
|
||||
|
||||
<span class="sd"> Reference: </span>
|
||||
<span class="sd"> https://iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201610_1.pdf</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">exif</span> <span class="o">=</span> <span class="p">(</span>
|
||||
@@ -1480,30 +1538,34 @@
|
||||
<span class="n">description</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">rendered</span><span class="p">)</span> <span class="k">if</span> <span class="n">rendered</span> <span class="k">else</span> <span class="s2">""</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ImageDescription"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Description"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:Caption-Abstract"</span><span class="p">]</span> <span class="o">=</span> <span class="n">description</span>
|
||||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:ImageDescription"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Description"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:Caption-Abstract"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">description</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Title"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"IPTC:ObjectName"</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span>
|
||||
|
||||
<span class="n">keyword_list</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="n">merge_exif_keywords</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_keywords</span><span class="p">())</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">replace_keywords</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">)</span>
|
||||
|
||||
<span class="n">person_list</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="n">merge_exif_persons</span><span class="p">:</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
||||
<span class="k">if</span> <span class="n">persons</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">merge_exif_persons</span><span class="p">:</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
||||
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">])</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
||||
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">])</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">use_albums_as_keywords</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
|
||||
@@ -1534,8 +1596,8 @@
|
||||
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">long_str</span><span class="p">)</span> <span class="o">></span> <span class="n">_MAX_IPTC_KEYWORD_LEN</span>
|
||||
<span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">long_keywords</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"Some keywords exceed max IPTC Keyword length of </span><span class="si">{</span><span class="n">_MAX_IPTC_KEYWORD_LEN</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">long_keywords</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"Warning: some keywords exceed max IPTC Keyword length of </span><span class="si">{</span><span class="n">_MAX_IPTC_KEYWORD_LEN</span><span class="si">}</span><span class="s2"> (exiftool will truncate these): </span><span class="si">{</span><span class="n">long_keywords</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered_keywords</span><span class="p">)</span>
|
||||
@@ -1547,25 +1609,26 @@
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:Subject"</span><span class="p">]</span> <span class="o">=</span> <span class="n">keyword_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:TagsList"</span><span class="p">]</span> <span class="o">=</span> <span class="n">keyword_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">persons</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">person_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">person_list</span><span class="p">)))</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"XMP:PersonInImage"</span><span class="p">]</span> <span class="o">=</span> <span class="n">person_list</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># if self.favorite():</span>
|
||||
<span class="c1"># exif["Rating"] = 5</span>
|
||||
|
||||
<span class="p">(</span><span class="n">lat</span><span class="p">,</span> <span class="n">lon</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="k">if</span> <span class="n">lat</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">lon</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon</span>
|
||||
<span class="n">lat_ref</span> <span class="o">=</span> <span class="s2">"N"</span> <span class="k">if</span> <span class="n">lat</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"S"</span>
|
||||
<span class="n">lon_ref</span> <span class="o">=</span> <span class="s2">"E"</span> <span class="k">if</span> <span class="n">lon</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"W"</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat_ref</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon_ref</span>
|
||||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"Keys:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"UserData:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="k">if</span> <span class="n">location</span><span class="p">:</span>
|
||||
<span class="p">(</span><span class="n">lat</span><span class="p">,</span> <span class="n">lon</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="k">if</span> <span class="n">lat</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">lon</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitude"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon</span>
|
||||
<span class="n">lat_ref</span> <span class="o">=</span> <span class="s2">"N"</span> <span class="k">if</span> <span class="n">lat</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"S"</span>
|
||||
<span class="n">lon_ref</span> <span class="o">=</span> <span class="s2">"E"</span> <span class="k">if</span> <span class="n">lon</span> <span class="o">>=</span> <span class="mi">0</span> <span class="k">else</span> <span class="s2">"W"</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLatitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lat_ref</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"EXIF:GPSLongitudeRef"</span><span class="p">]</span> <span class="o">=</span> <span class="n">lon_ref</span>
|
||||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismovie</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"Keys:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"UserData:GPSCoordinates"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">lat</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">lon</span><span class="si">}</span><span class="s2">"</span>
|
||||
|
||||
<span class="c1"># process date/time and timezone offset</span>
|
||||
<span class="c1"># Photos exports the following fields and sets modify date to creation date</span>
|
||||
@@ -1624,6 +1687,13 @@
|
||||
<span class="n">exif</span><span class="p">[</span><span class="s2">"QuickTime:ModifyDate"</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime_tz_to_utc</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">date_modified</span>
|
||||
<span class="p">)</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y:%m:</span><span class="si">%d</span><span class="s2"> %H:%M:%S"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># remove any new lines in any fields</span>
|
||||
<span class="k">for</span> <span class="n">field</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">exif</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||||
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">==</span> <span class="nb">str</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="n">field</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">" "</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="nb">type</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
|
||||
<span class="n">exif</span><span class="p">[</span><span class="n">field</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">" "</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">val</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">]</span>
|
||||
<span class="k">return</span> <span class="n">exif</span>
|
||||
|
||||
|
||||
@@ -1673,6 +1743,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</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="kc">None</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.</span>
|
||||
<span class="sd"> Does not include all the EXIF fields as those are likely already in the image.</span>
|
||||
@@ -1687,13 +1760,18 @@
|
||||
<span class="sd"> merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> filename: filename of the destination image file for including in exiftool signature in JSON sidecar</span>
|
||||
<span class="sd"> persons: if True, include person data</span>
|
||||
<span class="sd"> location: if True, include location data</span>
|
||||
<span class="sd"> replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
||||
|
||||
<span class="sd"> Returns: dict with exiftool tags / values</span>
|
||||
|
||||
<span class="sd"> Exports the following:</span>
|
||||
<span class="sd"> EXIF:ImageDescription</span>
|
||||
<span class="sd"> XMP:Description (may include template)</span>
|
||||
<span class="sd"> IPTC:CaptionAbstract</span>
|
||||
<span class="sd"> XMP:Title</span>
|
||||
<span class="sd"> IPTC:ObjectName</span>
|
||||
<span class="sd"> XMP:TagsList</span>
|
||||
<span class="sd"> IPTC:Keywords (may include album name, person name, or template)</span>
|
||||
<span class="sd"> XMP:Subject (set to keywords + person)</span>
|
||||
@@ -1721,6 +1799,9 @@
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="n">merge_exif_keywords</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="n">merge_exif_persons</span><span class="p">,</span>
|
||||
<span class="n">filename</span><span class="o">=</span><span class="n">filename</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">persons</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="n">replace_keywords</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">tag_groups</span><span class="p">:</span>
|
||||
@@ -1743,6 +1824,9 @@
|
||||
<span class="n">extension</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">merge_exif_persons</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="n">persons</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="n">replace_keywords</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="sd">"""returns string for XMP sidecar</span>
|
||||
<span class="sd"> use_albums_as_keywords: treat album names as keywords</span>
|
||||
@@ -1752,6 +1836,9 @@
|
||||
<span class="sd"> extension: which extension to use for SidecarForExtension property</span>
|
||||
<span class="sd"> merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)</span>
|
||||
<span class="sd"> persons: if True, include person data</span>
|
||||
<span class="sd"> location: if True, include location data</span>
|
||||
<span class="sd"> replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">xmp_template_file</span> <span class="o">=</span> <span class="p">(</span>
|
||||
@@ -1775,22 +1862,23 @@
|
||||
<span class="k">if</span> <span class="n">merge_exif_keywords</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_keywords</span><span class="p">())</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">keywords</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">replace_keywords</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">keywords</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># TODO: keyword handling in this and _exiftool_json_sidecar is</span>
|
||||
<span class="c1"># good candidate for pulling out in a function</span>
|
||||
|
||||
<span class="n">person_list</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="n">merge_exif_persons</span><span class="p">:</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
||||
<span class="k">if</span> <span class="n">persons</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">merge_exif_persons</span><span class="p">:</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_get_exif_persons</span><span class="p">())</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
||||
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">])</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span><span class="p">:</span>
|
||||
<span class="c1"># filter out _UNKNOWN_PERSON</span>
|
||||
<span class="n">person_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">persons</span> <span class="k">if</span> <span class="n">p</span> <span class="o">!=</span> <span class="n">_UNKNOWN_PERSON</span><span class="p">])</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">use_persons_as_keywords</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">person_list</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">use_albums_as_keywords</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
|
||||
@@ -1814,28 +1902,20 @@
|
||||
<span class="k">if</span> <span class="n">_OSXPHOTOS_NONE_SENTINEL</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">keyword</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="c1"># check to see if any keywords too long</span>
|
||||
<span class="n">long_keywords</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="n">long_str</span>
|
||||
<span class="k">for</span> <span class="n">long_str</span> <span class="ow">in</span> <span class="n">rendered_keywords</span>
|
||||
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">long_str</span><span class="p">)</span> <span class="o">></span> <span class="n">_MAX_IPTC_KEYWORD_LEN</span>
|
||||
<span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">long_keywords</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"Some keywords exceed max IPTC Keyword length of </span><span class="si">{</span><span class="n">_MAX_IPTC_KEYWORD_LEN</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">long_keywords</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">keyword_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">rendered_keywords</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># remove duplicates</span>
|
||||
<span class="c1"># sorted mainly to make testing the XMP file easier</span>
|
||||
<span class="k">if</span> <span class="n">keyword_list</span><span class="p">:</span>
|
||||
<span class="n">keyword_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">keyword_list</span><span class="p">)))</span>
|
||||
<span class="k">if</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">persons</span> <span class="ow">and</span> <span class="n">person_list</span><span class="p">:</span>
|
||||
<span class="n">person_list</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">person_list</span><span class="p">)))</span>
|
||||
|
||||
<span class="n">subject_list</span> <span class="o">=</span> <span class="n">keyword_list</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">location</span><span class="p">:</span>
|
||||
<span class="n">latlon</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
|
||||
<span class="n">xmp_str</span> <span class="o">=</span> <span class="n">xmp_template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span>
|
||||
<span class="n">photo</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">description</span><span class="o">=</span><span class="n">description</span><span class="p">,</span>
|
||||
@@ -1843,6 +1923,7 @@
|
||||
<span class="n">persons</span><span class="o">=</span><span class="n">person_list</span><span class="p">,</span>
|
||||
<span class="n">subjects</span><span class="o">=</span><span class="n">subject_list</span><span class="p">,</span>
|
||||
<span class="n">extension</span><span class="o">=</span><span class="n">extension</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">latlon</span><span class="p">,</span>
|
||||
<span class="n">version</span><span class="o">=</span><span class="n">__version__</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
@@ -1923,7 +2004,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photoinfo._photoinfo_scoreinfo — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photoinfo._photoinfo_scoreinfo — osxphotos 0.41.4 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -208,7 +208,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photoinfo._photoinfo_searchinfo — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photoinfo._photoinfo_searchinfo — osxphotos 0.41.4 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -366,7 +366,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photoinfo.photoinfo — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photoinfo.photoinfo — osxphotos 0.41.6 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -60,6 +60,7 @@
|
||||
<span class="n">_PHOTOS_5_SHARED_PHOTO_PATH</span><span class="p">,</span>
|
||||
<span class="n">_PHOTOS_5_VERSION</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="kn">from</span> <span class="nn">..adjustmentsinfo</span> <span class="kn">import</span> <span class="n">AdjustmentsInfo</span>
|
||||
<span class="kn">from</span> <span class="nn">..albuminfo</span> <span class="kn">import</span> <span class="n">AlbumInfo</span><span class="p">,</span> <span class="n">ImportInfo</span>
|
||||
<span class="kn">from</span> <span class="nn">..personinfo</span> <span class="kn">import</span> <span class="n">FaceInfo</span><span class="p">,</span> <span class="n">PersonInfo</span>
|
||||
<span class="kn">from</span> <span class="nn">..phototemplate</span> <span class="kn">import</span> <span class="n">PhotoTemplate</span>
|
||||
@@ -103,6 +104,7 @@
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span> <span class="o">=</span> <span class="n">uuid</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_info</span> <span class="o">=</span> <span class="n">info</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span> <span class="o">=</span> <span class="n">db</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_verbose</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">filename</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
@@ -484,9 +486,24 @@
|
||||
<span class="p">)</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_albums</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">burst_albums</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""If photo is non-selected burst photo, list of albums any other images in the same burst set are contained in, otherwise returns self.albums"""</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_selected</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span>
|
||||
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="n">burst_albums</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
|
||||
<span class="n">burst_albums</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">albums</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_albums</span><span class="p">))</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_albums</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" list of AlbumInfo objects representing albums the photos is contained in """</span>
|
||||
<span class="sd">""" list of AlbumInfo objects representing albums the photo is contained in """</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
@@ -496,6 +513,21 @@
|
||||
<span class="p">]</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_album_info</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">burst_album_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" If photo is a non-selected burst photo, returns list of AlbumInfo objects representing albums any other photos in the same burst set are contained in, otherwise returns self.album_info """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_selected</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span>
|
||||
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="n">burst_album_info</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst_photos</span><span class="p">:</span>
|
||||
<span class="n">burst_album_info</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">album_info</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">burst_album_info</span><span class="p">))</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_burst_album_info</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">import_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" ImportInfo object representing import session for the photo or None if no import session """</span>
|
||||
@@ -542,6 +574,30 @@
|
||||
<span class="sd">""" True if picture has adjustments / edits """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"hasAdjustments"</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">adjustments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" Returns AdjustmentsInfo class for adjustment data or None if no adjustments; Photos 5+ only """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">None</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="n">library</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span>
|
||||
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
|
||||
<span class="n">plist_file</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">library</span><span class="p">)</span>
|
||||
<span class="o">/</span> <span class="s2">"resources"</span>
|
||||
<span class="o">/</span> <span class="s2">"renders"</span>
|
||||
<span class="o">/</span> <span class="n">directory</span>
|
||||
<span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2">.plist"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">plist_file</span><span class="o">.</span><span class="n">is_file</span><span class="p">():</span>
|
||||
<span class="k">return</span> <span class="kc">None</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span> <span class="o">=</span> <span class="n">AdjustmentsInfo</span><span class="p">(</span><span class="n">plist_file</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_adjustmentinfo</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">external_edit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" Returns True if picture was edited outside of Photos using external editor """</span>
|
||||
@@ -687,6 +743,11 @@
|
||||
<span class="sd">""" Returns True if photo is part of a Burst photo set, otherwise False """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"burst"</span><span class="p">]</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">burst_selected</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"burst_key"</span><span class="p">]</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">burst_photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""If photo is a burst photo, returns list of PhotoInfo objects</span>
|
||||
@@ -855,8 +916,19 @@
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">orientation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">""" returns EXIF orientation of the current photo version as int """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"orientation"</span><span class="p">]</span>
|
||||
<span class="sd">""" returns EXIF orientation of the current photo version as int or 0 if current orientation cannot be determined """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"orientation"</span><span class="p">]</span>
|
||||
|
||||
<span class="c1"># For Photos 5+, try to get the adjusted orientation</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">hasadjustments</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">adjustments</span><span class="o">.</span><span class="n">adj_orientation</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># can't reliably determine orientation for edited photo if adjustmentinfo not available</span>
|
||||
<span class="k">return</span> <span class="mi">0</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"orientation"</span><span class="p">]</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">original_height</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
@@ -1184,7 +1256,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.photosdb.photosdb — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos.photosdb.photosdb — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
@@ -1861,7 +1861,6 @@
|
||||
|
||||
<span class="c1"># get details about photos</span>
|
||||
<span class="n">verbose</span><span class="p">(</span><span class="s2">"Processing photo details."</span><span class="p">)</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Getting information about photos"</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="sa">f</span><span class="s2">"""SELECT </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID, </span>
|
||||
<span class="s2"> ZADDITIONALASSETATTRIBUTES.ZMASTERFINGERPRINT, </span>
|
||||
@@ -2074,6 +2073,7 @@
|
||||
<span class="c1"># > 6 = portrait (sometimes, see ZDEPTHSTATE/ZDEPTHTYPE)</span>
|
||||
<span class="n">info</span><span class="p">[</span><span class="s2">"customRenderedValue"</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">22</span><span class="p">]</span>
|
||||
<span class="n">info</span><span class="p">[</span><span class="s2">"hdr"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">row</span><span class="p">[</span><span class="mi">22</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span> <span class="k">else</span> <span class="kc">False</span>
|
||||
<span class="n">info</span><span class="p">[</span><span class="s2">"depth_state"</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">36</span><span class="p">]</span>
|
||||
<span class="n">info</span><span class="p">[</span><span class="s2">"portrait"</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">row</span><span class="p">[</span><span class="mi">36</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span> <span class="k">else</span> <span class="kc">False</span>
|
||||
|
||||
<span class="c1"># Set panorama from either KindSubType or RenderedValue</span>
|
||||
@@ -2768,8 +2768,6 @@
|
||||
<span class="c1"># an empty album will be in _dbalbum_titles but not _dbalbums_album</span>
|
||||
<span class="k">pass</span>
|
||||
<span class="n">album_set</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">title_set</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not find album '</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">' in database"</span><span class="p">)</span>
|
||||
<span class="n">photos_sets</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">album_set</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">uuid</span><span class="p">:</span>
|
||||
@@ -2777,8 +2775,6 @@
|
||||
<span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">uuid</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">u</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">:</span>
|
||||
<span class="n">uuid_set</span><span class="o">.</span><span class="n">update</span><span class="p">([</span><span class="n">u</span><span class="p">])</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not find uuid '</span><span class="si">{</span><span class="n">u</span><span class="si">}</span><span class="s2">' in database"</span><span class="p">)</span>
|
||||
<span class="n">photos_sets</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">uuid_set</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">keywords</span><span class="p">:</span>
|
||||
@@ -2786,8 +2782,6 @@
|
||||
<span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">keywords</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dbkeywords_keyword</span><span class="p">:</span>
|
||||
<span class="n">keyword_set</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbkeywords_keyword</span><span class="p">[</span><span class="n">keyword</span><span class="p">])</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not find keyword '</span><span class="si">{</span><span class="n">keyword</span><span class="si">}</span><span class="s2">' in database"</span><span class="p">)</span>
|
||||
<span class="n">photos_sets</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">keyword_set</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">persons</span><span class="p">:</span>
|
||||
@@ -2800,8 +2794,6 @@
|
||||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||||
<span class="c1"># some persons have zero photos so they won't be in _dbfaces_pk</span>
|
||||
<span class="k">pass</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Could not find person '</span><span class="si">{</span><span class="n">person</span><span class="si">}</span><span class="s2">' in database"</span><span class="p">)</span>
|
||||
<span class="n">photos_sets</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">person_set</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">from_date</span> <span class="ow">or</span> <span class="n">to_date</span><span class="p">:</span> <span class="c1"># sourcery off</span>
|
||||
@@ -2812,14 +2804,10 @@
|
||||
<span class="n">dsel</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">dsel</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">v</span><span class="p">[</span><span class="s2">"imageDate"</span><span class="p">]</span> <span class="o">>=</span> <span class="n">from_date</span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"Found %i items with from_date </span><span class="si">{</span><span class="n">from_date</span><span class="si">}</span><span class="s2">"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">dsel</span><span class="p">)</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">to_date</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">datetime_has_tz</span><span class="p">(</span><span class="n">to_date</span><span class="p">):</span>
|
||||
<span class="n">to_date</span> <span class="o">=</span> <span class="n">datetime_naive_to_local</span><span class="p">(</span><span class="n">to_date</span><span class="p">)</span>
|
||||
<span class="n">dsel</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">dsel</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">v</span><span class="p">[</span><span class="s2">"imageDate"</span><span class="p">]</span> <span class="o"><=</span> <span class="n">to_date</span><span class="p">}</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Found %i items with to_date </span><span class="si">{</span><span class="n">to_date</span><span class="si">}</span><span class="s2">"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">dsel</span><span class="p">))</span>
|
||||
<span class="n">photos_sets</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">dsel</span><span class="o">.</span><span class="n">keys</span><span class="p">()))</span>
|
||||
|
||||
<span class="n">photoinfo</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
@@ -2951,7 +2939,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
7
docs/_static/doctools.js
vendored
@@ -29,9 +29,14 @@ if (!window.console || !console.firebug) {
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
if (!x) {
|
||||
return x
|
||||
}
|
||||
return decodeURIComponent(x.replace(/\+/g, ' '));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
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.40.8',
|
||||
VERSION: '0.41.10',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
4
docs/_static/language_data.js
vendored
@@ -13,7 +13,8 @@
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/* Non-minified version is copied as a separate JS file, is available */
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
@@ -199,7 +200,6 @@ var Stemmer = function() {
|
||||
|
||||
|
||||
|
||||
|
||||
var splitChars = (function() {
|
||||
var result = {};
|
||||
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
|
||||
|
||||
6
docs/_static/pygments.css
vendored
@@ -1,7 +1,7 @@
|
||||
pre { line-height: 125%; }
|
||||
td.linenos pre { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #f8f8f8; }
|
||||
|
||||
26
docs/_static/searchtools.js
vendored
@@ -248,7 +248,7 @@ var Search = {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
var listItem = $('<li></li>');
|
||||
var requestUrl = "";
|
||||
var linkUrl = "";
|
||||
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
|
||||
@@ -273,9 +273,9 @@ var Search = {
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
setTimeout(function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}, 5);
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
$.ajax({url: requestUrl,
|
||||
dataType: "text",
|
||||
@@ -285,16 +285,16 @@ var Search = {
|
||||
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
|
||||
}
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
setTimeout(function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}, 5);
|
||||
}});
|
||||
} else {
|
||||
// no source available, just display title
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
setTimeout(function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
// search finished, update title and status message
|
||||
@@ -379,6 +379,13 @@ var Search = {
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
|
||||
*/
|
||||
escapeRegExp : function(string) {
|
||||
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
},
|
||||
|
||||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
@@ -402,13 +409,14 @@ var Search = {
|
||||
];
|
||||
// add support for partial matches
|
||||
if (word.length > 2) {
|
||||
var word_regex = this.escapeRegExp(word);
|
||||
for (var w in terms) {
|
||||
if (w.match(word) && !terms[word]) {
|
||||
if (w.match(word_regex) && !terms[word]) {
|
||||
_o.push({files: terms[w], score: Scorer.partialTerm})
|
||||
}
|
||||
}
|
||||
for (var w in titleterms) {
|
||||
if (w.match(word) && !titleterms[word]) {
|
||||
if (w.match(word_regex) && !titleterms[word]) {
|
||||
_o.push({files: titleterms[w], score: Scorer.partialTitle})
|
||||
}
|
||||
}
|
||||
|
||||
2027
docs/_static/underscore-1.12.0.js
vendored
Normal file
37
docs/_static/underscore.js
vendored
482
docs/cli.html
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Index — osxphotos 0.40.8 documentation</title>
|
||||
<title>Index — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
@@ -327,6 +327,15 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-from-date">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-from-date">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--from-time <from_time>
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-from-time">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-from-time">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -509,6 +518,15 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-missing">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-missing">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--name <FILENAME>
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-name">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-name">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -529,8 +547,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-description">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--no-likes
|
||||
|
||||
@@ -540,6 +556,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-likes">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--no-place
|
||||
|
||||
@@ -776,6 +794,22 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-portrait">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-portrait">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--query-eval <CRITERIA>
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-query-eval">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-query-eval">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--replace-keywords
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -783,6 +817,13 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-report">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--retry <RETRY>
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-retry">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -909,6 +950,15 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-to-date">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-to-date">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--to-time <to_time>
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-to-time">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-to-time">osxphotos-query command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1017,6 +1067,8 @@
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.SearchInfo.activities">activities() (osxphotos.PhotoInfo.SearchInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.adjustments">adjustments() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.album_info">album_info() (osxphotos.PhotoInfo property)</a>
|
||||
|
||||
@@ -1032,10 +1084,10 @@
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums">(osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_as_dict">albums_as_dict() (osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_as_dict">albums_as_dict() (osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_shared">albums_shared() (osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_shared_as_dict">albums_shared_as_dict() (osxphotos.PhotosDB property)</a>
|
||||
@@ -1062,13 +1114,19 @@
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.ExifInfo.bit_rate">bit_rate (osxphotos.PhotoInfo.ExifInfo attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.SearchInfo.bodies_of_water">bodies_of_water() (osxphotos.PhotoInfo.SearchInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.burst">burst() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.burst_album_info">burst_album_info() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.burst_albums">burst_albums() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.burst_photos">burst_photos() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.burst_selected">burst_selected() (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
@@ -1461,6 +1519,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-folder">--folder <FOLDER></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-from-date">--from-date <from_date></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-from-time">--from-time <from_time></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-has-comment">--has-comment</a>
|
||||
</li>
|
||||
@@ -1497,6 +1557,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-load-config">--load-config <config file path></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-missing">--missing</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-name">--name <FILENAME></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-comment">--no-comment</a>
|
||||
</li>
|
||||
@@ -1553,8 +1615,14 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-place">--place <PLACE></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-portrait">--portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-query-eval">--query-eval <CRITERIA></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">--replace-keywords</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-report">--report <path to export report></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-retry">--retry <RETRY></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-save-config">--save-config <config file path></a>
|
||||
</li>
|
||||
@@ -1587,6 +1655,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-title">--title <TITLE></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-to-date">--to-date <to_date></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-to-time">--to-time <to_time></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-touch-file">--touch-file</a>
|
||||
</li>
|
||||
@@ -1713,6 +1783,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-folder">--folder <FOLDER></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-from-date">--from-date <from_date></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-from-time">--from-time <from_time></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-has-comment">--has-comment</a>
|
||||
</li>
|
||||
@@ -1741,6 +1813,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-live">--live</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-missing">--missing</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-name">--name <FILENAME></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-comment">--no-comment</a>
|
||||
</li>
|
||||
@@ -1795,6 +1869,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-place">--place <PLACE></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-portrait">--portrait</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-query-eval">--query-eval <CRITERIA></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-screenshot">--screenshot</a>
|
||||
</li>
|
||||
@@ -1809,6 +1885,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-title">--title <TITLE></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-to-date">--to-date <to_date></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-to-time">--to-time <to_time></a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-uti">--uti <UTI></a>
|
||||
</li>
|
||||
@@ -2108,7 +2186,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Welcome to osxphotos’s documentation! — osxphotos 0.40.8 documentation</title>
|
||||
<title>Welcome to osxphotos’s documentation! — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
@@ -365,7 +365,7 @@ Alternatively, you can also run the command line utility like this: <code class=
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos — osxphotos 0.40.8 documentation</title>
|
||||
<title>osxphotos — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
@@ -91,7 +91,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
|
||||
BIN
docs/objects.inv
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Search — osxphotos 0.40.8 documentation</title>
|
||||
<title>Search — osxphotos 0.41.10 documentation</title>
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
©2021, Rhet Turnbull.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.5.2</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ import importlib
|
||||
pathex = os.getcwd()
|
||||
|
||||
# include necessary data files
|
||||
datas=[('osxphotos/templates/xmp_sidecar.mako', 'osxphotos/templates'), ('osxphotos/templates/xmp_sidecar_beta.mako', 'osxphotos/templates')]
|
||||
datas=[('osxphotos/templates/xmp_sidecar.mako', 'osxphotos/templates'), ('osxphotos/templates/xmp_sidecar_beta.mako', 'osxphotos/templates'), ('osxphotos/phototemplate.tx', 'osxphotos'), ('osxphotos/phototemplate.md', 'osxphotos')]
|
||||
package_imports = [['photoscript', ['photoscript.applescript']]]
|
||||
for package, files in package_imports:
|
||||
proot = os.path.dirname(importlib.import_module(package).__file__)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.40.9"
|
||||
__version__ = "0.41.11"
|
||||
|
||||
174
osxphotos/adjustmentsinfo.py
Normal file
@@ -0,0 +1,174 @@
|
||||
""" AdjustmentsInfo class to read adjustments data for photos edited in Apple's Photos.app
|
||||
In Catalina and Big Sur, the adjustments data (data about edits done to the photo)
|
||||
is stored in a plist file in
|
||||
~/Pictures/Photos Library.photoslibrary/resources/renders/X/UUID.plist
|
||||
where X is first character of the photo's UUID string and UUID is the full UUID,
|
||||
e.g.: ~/Pictures/Photos Library.photoslibrary/resources/renders/3/30362C1D-192F-4CCD-9A2A-968F436DC0DE.plist
|
||||
|
||||
Thanks to @neilpa who figured out how to decode this information:
|
||||
Reference: https://github.com/neilpa/photohack/issues/4
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import plistlib
|
||||
import zlib
|
||||
|
||||
from .datetime_utils import datetime_naive_to_utc
|
||||
|
||||
|
||||
class AdjustmentsDecodeError(Exception):
|
||||
"""Could not decode adjustments plist file"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
class AdjustmentsInfo:
|
||||
def __init__(self, plist_file):
|
||||
self._plist_file = plist_file
|
||||
self._plist = self._load_plist_file(plist_file)
|
||||
|
||||
self._base_version = self._plist.get("adjustmentBaseVersion", None)
|
||||
self._data = self._plist.get("adjustmentData", None)
|
||||
self._editor_bundle_id = self._plist.get("adjustmentEditorBundleID", None)
|
||||
self._format_identifier = self._plist.get("adjustmentFormatIdentifier", None)
|
||||
self._format_version = self._plist.get("adjustmentFormatVersion")
|
||||
self._timestamp = self._plist.get("adjustmentTimestamp", None)
|
||||
if self._timestamp and type(self._timestamp) == datetime.datetime:
|
||||
self._timestamp = datetime_naive_to_utc(self._timestamp)
|
||||
|
||||
try:
|
||||
self._adjustments = self._decode_adjustments_from_plist(self._plist)
|
||||
except Exception as e:
|
||||
self._adjustments = None
|
||||
|
||||
def _decode_adjustments_from_plist(self, plist):
|
||||
"""decode adjustmentData from Apple Photos adjustments
|
||||
|
||||
Args:
|
||||
plist: a plist dict as loaded by plistlib
|
||||
|
||||
Returns:
|
||||
decoded adjustmentsData as dict
|
||||
"""
|
||||
|
||||
return json.loads(
|
||||
zlib.decompress(plist["adjustmentData"], -zlib.MAX_WBITS).decode()
|
||||
)
|
||||
|
||||
def _load_plist_file(self, plist_file):
|
||||
"""Load plist file from disk
|
||||
|
||||
Args:
|
||||
plist_file: full path to plist file
|
||||
|
||||
Returns:
|
||||
plist as dict
|
||||
"""
|
||||
with open(str(plist_file), "rb") as fd:
|
||||
plist_dict = plistlib.load(fd)
|
||||
return plist_dict
|
||||
|
||||
@property
|
||||
def plist(self):
|
||||
"""The actual adjustments plist content as a dict """
|
||||
return self._plist
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""The raw adjustments data as a binary blob """
|
||||
return self._data
|
||||
|
||||
@property
|
||||
def editor(self):
|
||||
"""The editor bundle ID for app/plug-in which made the adjustments """
|
||||
return self._editor_bundle_id
|
||||
|
||||
@property
|
||||
def format_id(self):
|
||||
"""The value of the adjustmentFormatIdentifier field in the plist """
|
||||
return self._format_identifier
|
||||
|
||||
@property
|
||||
def base_version(self):
|
||||
"""Value of adjustmentBaseVersion field """
|
||||
return self._base_version
|
||||
|
||||
@property
|
||||
def format_version(self):
|
||||
"""The value of the adjustmentFormatVersion in the plist """
|
||||
return self._format_version
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
"""The time stamp of the adjustment as timezone aware datetime.datetime object or None if no timestamp """
|
||||
return self._timestamp
|
||||
|
||||
@property
|
||||
def adjustments(self):
|
||||
"""List of adjustment dictionaries (or empty list if none or could not be decoded)"""
|
||||
try:
|
||||
return self._adjustments["adjustments"] if self._adjustments else []
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
@property
|
||||
def adj_metadata(self):
|
||||
"""Metadata dictionary or None if adjustment data could not be decoded"""
|
||||
try:
|
||||
return self._adjustments["metadata"] if self._adjustments else None
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def adj_orientation(self):
|
||||
"""EXIF orientation of image or 0 if none specified or None if adjustments could not be decoded"""
|
||||
try:
|
||||
return self._adjustments["metadata"]["orientation"]
|
||||
except KeyError:
|
||||
# no orientation field
|
||||
return 0
|
||||
except TypeError:
|
||||
# adjustments is None
|
||||
return 0
|
||||
|
||||
@property
|
||||
def adj_format_version(self):
|
||||
"""Format version for adjustments data (formatVersion field from adjustmentData) or None if adjustments could not be decoded"""
|
||||
try:
|
||||
return self._adjustments["formatVersion"] if self._adjustments else None
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def adj_version_info(self):
|
||||
"""version info for adjustments data or None if adjustments data could not be decoded"""
|
||||
try:
|
||||
return self._adjustments["versionInfo"] if self._adjustments else None
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def asdict(self):
|
||||
"""Returns all adjustments info as dictionary"""
|
||||
timestamp = self.timestamp
|
||||
if type(timestamp) == datetime.datetime:
|
||||
timestamp = timestamp.isoformat()
|
||||
|
||||
return {
|
||||
"data": self.data,
|
||||
"editor": self.editor,
|
||||
"format_id": self.format_id,
|
||||
"base_version": self.base_version,
|
||||
"format_version": self.format_version,
|
||||
"adjustments": self.adjustments,
|
||||
"metadata": self.adj_metadata,
|
||||
"orientation": self.adj_orientation,
|
||||
"adjustment_format_version": self.adj_format_version,
|
||||
"version_info": self.adj_version_info,
|
||||
"timestamp": timestamp,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f"AdjustmentsInfo(plist_file='{self._plist_file}')"
|
||||
599
osxphotos/cli.py
@@ -1,14 +1,23 @@
|
||||
"""Help text helper class for osxphotos CLI """
|
||||
|
||||
import io
|
||||
import re
|
||||
|
||||
import click
|
||||
import osxmetadata
|
||||
from rich.console import Console
|
||||
from rich.markdown import Markdown
|
||||
|
||||
from ._constants import (
|
||||
EXTENDED_ATTRIBUTE_NAMES,
|
||||
EXTENDED_ATTRIBUTE_NAMES_QUOTED,
|
||||
OSXPHOTOS_EXPORT_DB,
|
||||
)
|
||||
from .phototemplate import TEMPLATE_SUBSTITUTIONS, TEMPLATE_SUBSTITUTIONS_MULTI_VALUED
|
||||
from .phototemplate import (
|
||||
TEMPLATE_SUBSTITUTIONS,
|
||||
TEMPLATE_SUBSTITUTIONS_MULTI_VALUED,
|
||||
get_template_help,
|
||||
)
|
||||
|
||||
|
||||
class ExportCommand(click.Command):
|
||||
@@ -17,11 +26,11 @@ class ExportCommand(click.Command):
|
||||
def get_help(self, ctx):
|
||||
help_text = super().get_help(ctx)
|
||||
formatter = click.HelpFormatter()
|
||||
|
||||
# passed to click.HelpFormatter.write_dl for formatting
|
||||
|
||||
formatter.write("\n\n")
|
||||
formatter.write_text("** Export **")
|
||||
formatter.write(rich_text("[bold]** Export **[/bold]", width=formatter.width))
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"When exporting photos, osxphotos creates a database in the top-level "
|
||||
+ f"export folder called '{OSXPHOTOS_EXPORT_DB}'. This database preserves state information "
|
||||
@@ -56,7 +65,7 @@ class ExportCommand(click.Command):
|
||||
+ f"rebuilding the '{OSXPHOTOS_EXPORT_DB}' database."
|
||||
)
|
||||
formatter.write("\n\n")
|
||||
formatter.write_text("** Extended Attributes **")
|
||||
formatter.write(rich_text("[bold]** Extended Attributes **[/bold]", width=formatter.width))
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"""
|
||||
@@ -90,124 +99,9 @@ The following attributes may be used with '--xattr-template':
|
||||
"For additional information on extended attributes see: https://developer.apple.com/documentation/coreservices/file_metadata/mditem/common_metadata_attribute_keys"
|
||||
)
|
||||
formatter.write("\n\n")
|
||||
formatter.write_text("** Templating System **")
|
||||
formatter.write(rich_text("[bold]** Templating System **[/bold]", width=formatter.width))
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"""
|
||||
Several options, such as --directory, allow you to specify a template which
|
||||
will be rendered to substitute template fields with values from the photo.
|
||||
For example, '{created.month}' would be replaced with the month name of the
|
||||
photo creation date. e.g. 'November'.
|
||||
|
||||
Some options supporting templates may be repeated e.g., --keyword-template
|
||||
'{label}' --keyword-template '{media_type}' to add both labels and media
|
||||
types to the keywords.
|
||||
|
||||
The general format for a template is '{TEMPLATE_FIELD,DEFAULT}'. The full template format is:
|
||||
'{DELIM+TEMPLATE_FIELD(PATH_SEP)[OLD,NEW]?VALUE_IF_TRUE,DEFAULT}'
|
||||
|
||||
With a few exceptions (like '{created.strftime}') everything but the TEMPLATE_FIELD
|
||||
is optional.
|
||||
|
||||
- 'DELIM+' Multi-value template fields such as '{keyword}' may be expanded 'in place'
|
||||
with an optional delimiter using the template form '{DELIM+TEMPLATE_FIELD}'.
|
||||
For example, a photo with keywords 'foo' and 'bar':
|
||||
|
||||
'{keyword}' renders to 'foo' and 'bar'
|
||||
|
||||
'{,+keyword}' renders to: 'foo,bar'
|
||||
|
||||
'{; +keyword}' renders to: 'foo; bar'
|
||||
|
||||
'{+keyword}' renders to 'foobar'
|
||||
|
||||
- 'TEMPLATE_FIELD' The name of the template field, for example 'keyword'
|
||||
|
||||
- '(PATH_SEP)' Some template fields such as '{folder_album}' are "path-like" in
|
||||
that they join multiple elements into a single path-like string. For example,
|
||||
if photo is in album Album1 in folder Folder1, '{folder_album}' results in
|
||||
'Folder1/Album1'. This is so these template fields may be used as paths in
|
||||
--directory. If you intend to use such a field as a string, e.g. in the
|
||||
filename, you may specify a different path separator using the form:
|
||||
'{TEMPLATE_FIELD(PATH_SEP)}'. For example, using the example above,
|
||||
'{folder_album(-)}' would result in 'Folder1-Album1' and '{folder_album()}'
|
||||
would result in 'Folder1Album1'.
|
||||
|
||||
- '[OLD,NEW]' Use the [OLD,NEW] option to replace text "OLD" in the template value
|
||||
with text "NEW". For example, if you have album names with '/' in the album name you
|
||||
could replace '/' with "-" using the template '{album[/,-]}'. This would replace
|
||||
any occurence of "/" in the album name with "-"; album "Vacation/2019" would thus
|
||||
become "Vacation-2019". You may specify more than one pair of OLD,NEW values by
|
||||
listing them delimited by '|'. For example: '{album[/,-|:,-]}' to replace both
|
||||
'/' and ':' by '-'. You can also use the [OLD,NEW] syntax to delete a character by
|
||||
omitting the NEW value as in '{album[/,]}'.
|
||||
|
||||
- '?' Some template fields such as 'hdr' are boolean and resolve to True or False.
|
||||
These take the form: '{TEMPLATE_FIELD?VALUE_IF_TRUE,VALUE_IF_FALSE}', e.g.
|
||||
{hdr?is_hdr,not_hdr} which would result in 'is_hdr' if photo is an HDR image
|
||||
and 'not_hdr' otherwise.
|
||||
|
||||
- ',DEFAULT' The ',' and DEFAULT value are optional. If TEMPLATE_FIELD results
|
||||
in a null (empty) value, the template will result in default value of '_'.
|
||||
You may specify an alternate default value by appending ',DEFAULT' after
|
||||
template_field. Example: '{title,no_title}' would result in 'no_title' if the photo
|
||||
had no title. Example: '{created.year}/{place.address,NO_ADDRESS}' but there was
|
||||
no address associated with the photo, the resulting output would be:
|
||||
'2020/NO_ADDRESS/photoname.jpg'. If specified, the default value may not
|
||||
contain a brace symbol ('{' or '}').
|
||||
|
||||
Again, if you do not specify a default value and the template substitution has no
|
||||
value, '_' (underscore) will be used as the default value. For example, in the
|
||||
above example, this would result in '2020/_/photoname.jpg' if address was
|
||||
null.
|
||||
|
||||
You may specify a null default (e.g. "" or empty string) by omitting the value
|
||||
after the comma, e.g. {title,} which would render to "" if title had no value thus
|
||||
effectively deleting the template from the resulting string.
|
||||
|
||||
You may include other text in the template string outside the {}
|
||||
and use more than one template field in a single string,
|
||||
e.g. '{created.year} - {created.month}' (e.g. '2020 - November').
|
||||
|
||||
Some templates may resolve to more than one value. For example, a photo can
|
||||
have multiple keywords so '{keyword}' can result in multiple values. If used
|
||||
in a filename or directory, these templates may result in more than one copy
|
||||
of the photo being exported. For example, if photo has keywords "foo" and
|
||||
"bar", --directory '{keyword}' will result in copies of the photo being
|
||||
exported to 'foo/image_name.jpeg' and 'bar/image_name.jpeg'.
|
||||
|
||||
Some template fields such as '{media_type}' use the 'DEFAULT' value to allow
|
||||
customization of the output. For example, '{media_type}' resolves to the
|
||||
special media type of the photo such as 'panorama' or 'selfie'. You may use
|
||||
the 'DEFAULT' value to override these in form:
|
||||
'{media_type,video=vidéo;time_lapse=vidéo_accélérée}'. In this example, if
|
||||
photo is a time_lapse photo, 'media_type' would resolve to 'vidéo_accélérée'
|
||||
instead of 'time_lapse' and video would resolve to 'vidéo' if photo is an
|
||||
ordinary video.
|
||||
|
||||
With the --directory and --filename options you may specify a template for the
|
||||
export directory or filename, respectively. The directory will be appended to
|
||||
the export path specified in the export DEST argument to export. For example,
|
||||
if template is '{created.year}/{created.month}', and export destination DEST
|
||||
is '/Users/maria/Pictures/export', the actual export directory for a photo
|
||||
would be '/Users/maria/Pictures/export/2020/March' if the photo was created in
|
||||
March 2020.
|
||||
|
||||
The templating system may also be used with the --keyword-template option to
|
||||
set keywords on export (with --exiftool or --sidecar), for example, to set a
|
||||
new keyword in format 'folder/subfolder/album' to preserve the folder/album
|
||||
structure, you can use --keyword-template "{folder_album}"
|
||||
|
||||
In the template, valid template substitutions will be replaced by the
|
||||
corresponding value from the table below. Invalid substitutions will result
|
||||
in an error.
|
||||
|
||||
If you want the actual text of the template substition to appear in the
|
||||
rendered name, use double braces, e.g. '{{' or '}}', thus using
|
||||
'{created.year}/{{name}}' for --directory would result in output of
|
||||
2020/{name}/photoname.jpg
|
||||
"""
|
||||
)
|
||||
formatter.write(template_help(width=formatter.width))
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"With the --directory and --filename options you may specify a template for the "
|
||||
@@ -224,7 +118,8 @@ rendered name, use double braces, e.g. '{{' or '}}', thus using
|
||||
"The templating system may also be used with the --keyword-template option "
|
||||
+ "to set keywords on export (with --exiftool or --sidecar), "
|
||||
+ "for example, to set a new keyword in format 'folder/subfolder/album' to "
|
||||
+ 'preserve the folder/album structure, you can use --keyword-template "{folder_album}"'
|
||||
+ 'preserve the folder/album structure, you can use --keyword-template "{folder_album}" '
|
||||
+ "or in the 'folder>subfolder>album' format used in Lightroom Classic, --keyword-template \"{folder_album(>)}\"."
|
||||
)
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
@@ -233,33 +128,7 @@ rendered name, use double braces, e.g. '{{' or '}}', thus using
|
||||
+ "an error and the script will abort."
|
||||
)
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"If you want the actual text of the template substition to appear "
|
||||
+ "in the rendered name, use double braces, e.g. '{{' or '}}', thus "
|
||||
+ "using '{created.year}/{{name}}' for --directory "
|
||||
+ "would result in output of 2020/{name}/photoname.jpg"
|
||||
)
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"You may specify an optional default value to use if the substitution does not contain a value "
|
||||
+ "(e.g. the value is null) "
|
||||
+ "by specifying the default value after a ',' in the template string: "
|
||||
+ "for example, if template is '{created.year}/{place.address,NO_ADDRESS}' "
|
||||
+ "but there was no address associated with the photo, the resulting output would be: "
|
||||
+ "'2020/NO_ADDRESS/photoname.jpg'. "
|
||||
+ "If specified, the default value may not contain a brace symbol ('{' or '}')."
|
||||
)
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
"If you do not specify a default value and the template substitution "
|
||||
+ "has no value, '_' (underscore) will be used as the default value. For example, in the "
|
||||
+ "above example, this would result in '2020/_/photoname.jpg' if address was null."
|
||||
)
|
||||
formatter.write("\n")
|
||||
formatter.write_text(
|
||||
'You may specify a null default (e.g. "" or empty string) by omitting the value after '
|
||||
+ 'the comma, e.g. {title,} which would render to "" if title had no value.'
|
||||
)
|
||||
formatter.write(rich_text("[bold]** Template Substitutions **[/bold]", width=formatter.width))
|
||||
formatter.write("\n")
|
||||
templ_tuples = [("Substitution", "Description")]
|
||||
templ_tuples.extend((k, v) for k, v in TEMPLATE_SUBSTITUTIONS.items())
|
||||
@@ -284,3 +153,45 @@ rendered name, use double braces, e.g. '{{' or '}}', thus using
|
||||
formatter.write_dl(templ_tuples)
|
||||
help_text += formatter.getvalue()
|
||||
return help_text
|
||||
|
||||
|
||||
def template_help(width=78):
|
||||
"""Return formatted string for template system """
|
||||
sio = io.StringIO()
|
||||
console = Console(file=sio, force_terminal=True, width=width)
|
||||
template_help_md = strip_md_links(get_template_help())
|
||||
console.print(Markdown(template_help_md))
|
||||
help_str = sio.getvalue()
|
||||
sio.close()
|
||||
return help_str
|
||||
|
||||
|
||||
def rich_text(text, width=78):
|
||||
"""Return rich formatted text"""
|
||||
sio = io.StringIO()
|
||||
console = Console(file=sio, force_terminal=True, width=width)
|
||||
console.print(text)
|
||||
rich_text = sio.getvalue()
|
||||
sio.close()
|
||||
return rich_text
|
||||
|
||||
|
||||
def strip_md_links(md):
|
||||
"""strip markdown links from markdown text md
|
||||
|
||||
Args:
|
||||
md: str, markdown text
|
||||
|
||||
Returns:
|
||||
str with markdown links removed
|
||||
|
||||
Note: This uses a very basic regex that likely fails on all sorts of edge cases
|
||||
but works for the links in the osxphotos docs
|
||||
"""
|
||||
links = r"(?:[*#])|\[(.*?)\]\(.+?\)"
|
||||
|
||||
def subfn(match):
|
||||
return match.group(1)
|
||||
|
||||
return re.sub(links, subfn, md)
|
||||
|
||||
|
||||
@@ -176,6 +176,10 @@ class ExifTool:
|
||||
command = [f"-{tag}={value}"]
|
||||
if self.overwrite and not self._context_mgr:
|
||||
command.append("-overwrite_original")
|
||||
|
||||
# avoid "Warning: Some character(s) could not be encoded in Latin" warning
|
||||
command.append("-iptc:codedcharacterset=utf8")
|
||||
|
||||
if self._context_mgr:
|
||||
self._commands.extend(command)
|
||||
return True
|
||||
@@ -254,7 +258,11 @@ class ExifTool:
|
||||
filename = os.fsencode(self.file) if not no_file else b""
|
||||
|
||||
if self.flags:
|
||||
command_str = b"\n".join([f.encode("utf-8") for f in self.flags])
|
||||
# need to split flags, e.g. so "--ext AVI" becomes ["--ext", "AVI"]
|
||||
flags = []
|
||||
for f in self.flags:
|
||||
flags.extend(f.split())
|
||||
command_str = b"\n".join([f.encode("utf-8") for f in flags])
|
||||
command_str += b"\n"
|
||||
else:
|
||||
command_str = b""
|
||||
@@ -311,7 +319,13 @@ class ExifTool:
|
||||
if not json_str:
|
||||
return dict()
|
||||
|
||||
exifdict = json.loads(json_str)
|
||||
try:
|
||||
exifdict = json.loads(json_str)
|
||||
except Exception as e:
|
||||
# will fail with some commands, e.g --ext AVI which produces
|
||||
# 'No file with specified extension' instead of json
|
||||
return dict()
|
||||
|
||||
exifdict = exifdict[0]
|
||||
if not tag_groups:
|
||||
# strip tag groups
|
||||
|
||||
@@ -11,16 +11,15 @@ MPRI_Reg_Rect = namedtuple("MPRI_Reg_Rect", ["x", "y", "h", "w"])
|
||||
|
||||
|
||||
class PersonInfo:
|
||||
""" Info about a person in the Photos library
|
||||
"""
|
||||
"""Info about a person in the Photos library"""
|
||||
|
||||
def __init__(self, db=None, pk=None):
|
||||
""" Creates a new PersonInfo instance
|
||||
"""Creates a new PersonInfo instance
|
||||
|
||||
Arguments:
|
||||
db: instance of PhotosDB object
|
||||
pk: primary key value of person to initialize PersonInfo with
|
||||
|
||||
pk: primary key value of person to initialize PersonInfo with
|
||||
|
||||
Returns:
|
||||
PersonInfo instance
|
||||
"""
|
||||
@@ -57,8 +56,8 @@ class PersonInfo:
|
||||
|
||||
@property
|
||||
def face_info(self):
|
||||
""" Returns a list of FaceInfo objects associated with this person sorted by quality score
|
||||
Highest quality face is result[0] and lowest quality face is result[n]
|
||||
"""Returns a list of FaceInfo objects associated with this person sorted by quality score
|
||||
Highest quality face is result[0] and lowest quality face is result[n]
|
||||
"""
|
||||
try:
|
||||
faces = self._db._db_faceinfo_person[self._pk]
|
||||
@@ -103,16 +102,15 @@ class PersonInfo:
|
||||
|
||||
|
||||
class FaceInfo:
|
||||
""" Info about a face in the Photos library
|
||||
"""
|
||||
"""Info about a face in the Photos library"""
|
||||
|
||||
def __init__(self, db=None, pk=None):
|
||||
""" Creates a new FaceInfo instance
|
||||
"""Creates a new FaceInfo instance
|
||||
|
||||
Arguments:
|
||||
db: instance of PhotosDB object
|
||||
pk: primary key value of face to init the object with
|
||||
|
||||
pk: primary key value of face to init the object with
|
||||
|
||||
Returns:
|
||||
FaceInfo instance
|
||||
"""
|
||||
@@ -156,7 +154,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
""" Coordinates, in PIL format, for center of face
|
||||
"""Coordinates, in PIL format, for center of face
|
||||
|
||||
Returns:
|
||||
tuple of coordinates in form (x, y)
|
||||
@@ -165,7 +163,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def size_pixels(self):
|
||||
""" Size of face in pixels (centered around center_x, center_y)
|
||||
"""Size of face in pixels (centered around center_x, center_y)
|
||||
|
||||
Returns:
|
||||
size, in int pixels, of a circle drawn around the center of the face
|
||||
@@ -176,7 +174,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def mouth(self):
|
||||
""" Coordinates, in PIL format, for mouth position
|
||||
"""Coordinates, in PIL format, for mouth position
|
||||
|
||||
Returns:
|
||||
tuple of coordinates in form (x, y)
|
||||
@@ -185,7 +183,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def left_eye(self):
|
||||
""" Coordinates, in PIL format, for left eye position
|
||||
"""Coordinates, in PIL format, for left eye position
|
||||
|
||||
Returns:
|
||||
tuple of coordinates in form (x, y)
|
||||
@@ -194,7 +192,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def right_eye(self):
|
||||
""" Coordinates, in PIL format, for right eye position
|
||||
"""Coordinates, in PIL format, for right eye position
|
||||
|
||||
Returns:
|
||||
tuple of coordinates in form (x, y)
|
||||
@@ -223,7 +221,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def mwg_rs_area(self):
|
||||
""" Get coordinates for Metadata Working Group Region Area.
|
||||
"""Get coordinates for Metadata Working Group Region Area.
|
||||
|
||||
Returns:
|
||||
MWG_RS_Area named tuple with x, y, h, w where:
|
||||
@@ -249,7 +247,7 @@ class FaceInfo:
|
||||
|
||||
@property
|
||||
def mpri_reg_rect(self):
|
||||
""" Get coordinates for Microsoft Photo Region Rectangle.
|
||||
"""Get coordinates for Microsoft Photo Region Rectangle.
|
||||
|
||||
Returns:
|
||||
MPRI_Reg_Rect named tuple with x, y, h, w where:
|
||||
@@ -278,7 +276,7 @@ class FaceInfo:
|
||||
return MPRI_Reg_Rect(x, y, h, w)
|
||||
|
||||
def face_rect(self):
|
||||
""" Get face rectangle coordinates for current version of the associated image
|
||||
"""Get face rectangle coordinates for current version of the associated image
|
||||
If image has been edited, rectangle applies to edited version, otherwise original version
|
||||
Coordinates in format and reference frame used by PIL
|
||||
|
||||
@@ -321,12 +319,12 @@ class FaceInfo:
|
||||
return yaw
|
||||
|
||||
def _fix_orientation(self, xy):
|
||||
""" Translate an (x, y) tuple based on image orientation
|
||||
"""Translate an (x, y) tuple based on image orientation
|
||||
|
||||
Arguments:
|
||||
xy: tuple of (x, y) coordinates for point to translate
|
||||
in format used by Photos (percent of height/width)
|
||||
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of translated coordinates
|
||||
"""
|
||||
@@ -350,21 +348,24 @@ class FaceInfo:
|
||||
elif orientation == 7:
|
||||
x, y = y, x
|
||||
y = 1.0 - y
|
||||
elif orientation ==8:
|
||||
elif orientation == 8:
|
||||
x, y = y, x
|
||||
elif orientation == 0:
|
||||
# set by osxphotos if adjusted orientation cannot be read, assume it's 1
|
||||
y = 1.0 - y
|
||||
else:
|
||||
logging.warning(f"Unhandled orientation: {orientation}")
|
||||
|
||||
return (x, y)
|
||||
|
||||
def _make_point(self, xy):
|
||||
""" Translate an (x, y) tuple based on image orientation
|
||||
"""Translate an (x, y) tuple based on image orientation
|
||||
and convert to image coordinates
|
||||
|
||||
Arguments:
|
||||
xy: tuple of (x, y) coordinates for point to translate
|
||||
in format used by Photos (percent of height/width)
|
||||
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of translated coordinates in pixels in PIL format/reference frame
|
||||
"""
|
||||
@@ -379,13 +380,13 @@ class FaceInfo:
|
||||
return (int(x * dx), int(y * dy))
|
||||
|
||||
def _make_point_with_rotation(self, xy):
|
||||
""" Translate an (x, y) tuple based on image orientation and rotation
|
||||
"""Translate an (x, y) tuple based on image orientation and rotation
|
||||
and convert to image coordinates
|
||||
|
||||
Arguments:
|
||||
xy: tuple of (x, y) coordinates for point to translate
|
||||
in format used by Photos (percent of height/width)
|
||||
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of translated coordinates in pixels in PIL format/reference frame
|
||||
"""
|
||||
@@ -472,14 +473,14 @@ class FaceInfo:
|
||||
|
||||
|
||||
def rotate_image_point(x, y, xmid, ymid, angle):
|
||||
""" rotate image point about xm, ym by angle in radians
|
||||
"""rotate image point about xm, ym by angle in radians
|
||||
|
||||
Arguments:
|
||||
x: x coordinate of point to rotate
|
||||
x: x coordinate of point to rotate
|
||||
y: y coordinate of point to rotate
|
||||
xmid: x coordinate of center point to rotate about
|
||||
ymid: y coordinate of center point to rotate about
|
||||
angle: angle in radians about which to coordinate,
|
||||
angle: angle in radians about which to coordinate,
|
||||
counter-clockwise is positive
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -87,6 +87,8 @@ class ExportResults:
|
||||
exiftool_error=None,
|
||||
xattr_written=None,
|
||||
xattr_skipped=None,
|
||||
deleted_files=None,
|
||||
deleted_directories=None,
|
||||
):
|
||||
self.exported = exported or []
|
||||
self.new = new or []
|
||||
@@ -107,6 +109,8 @@ class ExportResults:
|
||||
self.exiftool_error = exiftool_error or []
|
||||
self.xattr_written = xattr_written or []
|
||||
self.xattr_skipped = xattr_skipped or []
|
||||
self.deleted_files = deleted_files or []
|
||||
self.deleted_directories = deleted_directories or []
|
||||
|
||||
def all_files(self):
|
||||
""" return all filenames contained in results """
|
||||
@@ -151,6 +155,8 @@ class ExportResults:
|
||||
self.error += other.error
|
||||
self.exiftool_warning += other.exiftool_warning
|
||||
self.exiftool_error += other.exiftool_error
|
||||
self.deleted_files += other.deleted_files
|
||||
self.deleted_directories += other.deleted_directories
|
||||
return self
|
||||
|
||||
def __str__(self):
|
||||
@@ -173,6 +179,8 @@ class ExportResults:
|
||||
+ f",error={self.error}"
|
||||
+ f",exiftool_warning={self.exiftool_warning}"
|
||||
+ f",exiftool_error={self.exiftool_error}"
|
||||
+ f",deleted_files={self.deleted_files}"
|
||||
+ f",deleted_directories={self.deleted_directories}"
|
||||
+ ")"
|
||||
)
|
||||
|
||||
@@ -475,6 +483,9 @@ def export2(
|
||||
merge_exif_keywords=False,
|
||||
merge_exif_persons=False,
|
||||
jpeg_ext=None,
|
||||
persons=True,
|
||||
location=True,
|
||||
replace_keywords=False,
|
||||
):
|
||||
"""export photo, like export but with update and dry_run options
|
||||
dest: must be valid destination path or exception raised
|
||||
@@ -527,6 +538,9 @@ def export2(
|
||||
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||
jpeg_ext: if set, will use this value for extension on jpegs converted to jpeg with convert_to_jpeg; if not set, uses jpeg; do not include the leading "."
|
||||
persons: if True, include persons in exported metadata
|
||||
location: if True, include location in exported metadata
|
||||
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
|
||||
|
||||
Returns: ExportResults class
|
||||
ExportResults has attributes:
|
||||
@@ -607,9 +621,9 @@ def export2(
|
||||
)
|
||||
edited_name = pathlib.Path(self.path_edited).name
|
||||
edited_suffix = pathlib.Path(edited_name).suffix
|
||||
fname = pathlib.Path(self.filename).stem + edited_identifier + edited_suffix
|
||||
fname = pathlib.Path(self.original_filename).stem + edited_identifier + edited_suffix
|
||||
else:
|
||||
fname = self.filename
|
||||
fname = self.original_filename
|
||||
|
||||
uti = self.uti if edited else self.uti_original
|
||||
if convert_to_jpeg and self.isphoto and uti != "public.jpeg":
|
||||
@@ -941,6 +955,9 @@ def export2(
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
filename=dest.name,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
sidecars.append(
|
||||
(
|
||||
@@ -964,6 +981,9 @@ def export2(
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
filename=dest.name,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
sidecars.append(
|
||||
(
|
||||
@@ -983,6 +1003,9 @@ def export2(
|
||||
keyword_template=keyword_template,
|
||||
description_template=description_template,
|
||||
extension=dest.suffix[1:] if dest.suffix else None,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
sidecars.append(
|
||||
(
|
||||
@@ -1050,6 +1073,9 @@ def export2(
|
||||
ignore_date_modified=ignore_date_modified,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
)[0]
|
||||
if old_data != current_data:
|
||||
@@ -1070,6 +1096,9 @@ def export2(
|
||||
flags=exiftool_flags,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
if warning_:
|
||||
all_results.exiftool_warning.append((exported_file, warning_))
|
||||
@@ -1087,6 +1116,9 @@ def export2(
|
||||
ignore_date_modified=ignore_date_modified,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
),
|
||||
)
|
||||
export_db.set_stat_exif_for_file(
|
||||
@@ -1109,6 +1141,9 @@ def export2(
|
||||
flags=exiftool_flags,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
if warning_:
|
||||
all_results.exiftool_warning.append((exported_file, warning_))
|
||||
@@ -1126,6 +1161,9 @@ def export2(
|
||||
ignore_date_modified=ignore_date_modified,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
),
|
||||
)
|
||||
export_db.set_stat_exif_for_file(
|
||||
@@ -1345,6 +1383,9 @@ def _write_exif_data(
|
||||
flags=None,
|
||||
merge_exif_keywords=False,
|
||||
merge_exif_persons=False,
|
||||
persons=True,
|
||||
location=True,
|
||||
replace_keywords=False,
|
||||
):
|
||||
"""write exif data to image file at filepath
|
||||
|
||||
@@ -1355,6 +1396,9 @@ def _write_exif_data(
|
||||
keyword_template: (list of strings); list of template strings to render as keywords
|
||||
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||
flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F)
|
||||
persons: if True, write person data to metadata
|
||||
location: if True, write location data to metadata
|
||||
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
|
||||
|
||||
Returns:
|
||||
(warning, error) of warning and error strings if exiftool produces warnings or errors
|
||||
@@ -1369,6 +1413,9 @@ def _write_exif_data(
|
||||
ignore_date_modified=ignore_date_modified,
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
|
||||
with ExifTool(filepath, flags=flags, exiftool=self._db._exiftool_path) as exiftool:
|
||||
@@ -1391,6 +1438,9 @@ def _exiftool_dict(
|
||||
merge_exif_keywords=False,
|
||||
merge_exif_persons=False,
|
||||
filename=None,
|
||||
persons=True,
|
||||
location=True,
|
||||
replace_keywords=False,
|
||||
):
|
||||
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
||||
Does not include all the EXIF fields as those are likely already in the image.
|
||||
@@ -1404,6 +1454,9 @@ def _exiftool_dict(
|
||||
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||
merge_exif_keywords: merge keywords in the file's exif metadata (requires exiftool)
|
||||
merge_exif_persons: merge persons in the file's exif metadata (requires exiftool)
|
||||
persons: if True, include person data
|
||||
location: if True, include location data
|
||||
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
|
||||
|
||||
Returns: dict with exiftool tags / values
|
||||
|
||||
@@ -1411,8 +1464,10 @@ def _exiftool_dict(
|
||||
EXIF:ImageDescription (may include template)
|
||||
XMP:Description (may include template)
|
||||
XMP:Title
|
||||
IPTC:ObjectName
|
||||
XMP:TagsList (may include album name, person name, or template)
|
||||
IPTC:Keywords (may include album name, person name, or template)
|
||||
IPTC:Caption-Abstract
|
||||
XMP:Subject (set to keywords + persons)
|
||||
XMP:PersonInImage
|
||||
EXIF:GPSLatitudeRef, EXIF:GPSLongitudeRef
|
||||
@@ -1428,6 +1483,9 @@ def _exiftool_dict(
|
||||
QuickTime:ModifyDate (UTC)
|
||||
QuickTime:GPSCoordinates
|
||||
UserData:GPSCoordinates
|
||||
|
||||
Reference:
|
||||
https://iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201610_1.pdf
|
||||
"""
|
||||
|
||||
exif = (
|
||||
@@ -1447,30 +1505,34 @@ def _exiftool_dict(
|
||||
description = " ".join(rendered) if rendered else ""
|
||||
exif["EXIF:ImageDescription"] = description
|
||||
exif["XMP:Description"] = description
|
||||
exif["IPTC:Caption-Abstract"] = description
|
||||
elif self.description:
|
||||
exif["EXIF:ImageDescription"] = self.description
|
||||
exif["XMP:Description"] = self.description
|
||||
exif["IPTC:Caption-Abstract"] = self.description
|
||||
|
||||
if self.title:
|
||||
exif["XMP:Title"] = self.title
|
||||
exif["IPTC:ObjectName"] = self.title
|
||||
|
||||
keyword_list = []
|
||||
if merge_exif_keywords:
|
||||
keyword_list.extend(self._get_exif_keywords())
|
||||
|
||||
if self.keywords:
|
||||
if self.keywords and not replace_keywords:
|
||||
keyword_list.extend(self.keywords)
|
||||
|
||||
person_list = []
|
||||
if merge_exif_persons:
|
||||
person_list.extend(self._get_exif_persons())
|
||||
if persons:
|
||||
if merge_exif_persons:
|
||||
person_list.extend(self._get_exif_persons())
|
||||
|
||||
if self.persons:
|
||||
# filter out _UNKNOWN_PERSON
|
||||
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||
if self.persons:
|
||||
# filter out _UNKNOWN_PERSON
|
||||
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||
|
||||
if use_persons_as_keywords and person_list:
|
||||
keyword_list.extend(person_list)
|
||||
if use_persons_as_keywords and person_list:
|
||||
keyword_list.extend(person_list)
|
||||
|
||||
if use_albums_as_keywords and self.albums:
|
||||
keyword_list.extend(self.albums)
|
||||
@@ -1514,25 +1576,26 @@ def _exiftool_dict(
|
||||
exif["XMP:Subject"] = keyword_list.copy()
|
||||
exif["XMP:TagsList"] = keyword_list.copy()
|
||||
|
||||
if person_list:
|
||||
if persons and person_list:
|
||||
person_list = sorted(list(set(person_list)))
|
||||
exif["XMP:PersonInImage"] = person_list.copy()
|
||||
|
||||
# if self.favorite():
|
||||
# exif["Rating"] = 5
|
||||
|
||||
(lat, lon) = self.location
|
||||
if lat is not None and lon is not None:
|
||||
if self.isphoto:
|
||||
exif["EXIF:GPSLatitude"] = lat
|
||||
exif["EXIF:GPSLongitude"] = lon
|
||||
lat_ref = "N" if lat >= 0 else "S"
|
||||
lon_ref = "E" if lon >= 0 else "W"
|
||||
exif["EXIF:GPSLatitudeRef"] = lat_ref
|
||||
exif["EXIF:GPSLongitudeRef"] = lon_ref
|
||||
elif self.ismovie:
|
||||
exif["Keys:GPSCoordinates"] = f"{lat} {lon}"
|
||||
exif["UserData:GPSCoordinates"] = f"{lat} {lon}"
|
||||
if location:
|
||||
(lat, lon) = self.location
|
||||
if lat is not None and lon is not None:
|
||||
if self.isphoto:
|
||||
exif["EXIF:GPSLatitude"] = lat
|
||||
exif["EXIF:GPSLongitude"] = lon
|
||||
lat_ref = "N" if lat >= 0 else "S"
|
||||
lon_ref = "E" if lon >= 0 else "W"
|
||||
exif["EXIF:GPSLatitudeRef"] = lat_ref
|
||||
exif["EXIF:GPSLongitudeRef"] = lon_ref
|
||||
elif self.ismovie:
|
||||
exif["Keys:GPSCoordinates"] = f"{lat} {lon}"
|
||||
exif["UserData:GPSCoordinates"] = f"{lat} {lon}"
|
||||
|
||||
# process date/time and timezone offset
|
||||
# Photos exports the following fields and sets modify date to creation date
|
||||
@@ -1591,6 +1654,13 @@ def _exiftool_dict(
|
||||
exif["QuickTime:ModifyDate"] = datetime_tz_to_utc(
|
||||
self.date_modified
|
||||
).strftime("%Y:%m:%d %H:%M:%S")
|
||||
|
||||
# remove any new lines in any fields
|
||||
for field, val in exif.items():
|
||||
if type(val) == str:
|
||||
exif[field] = val.replace("\n", " ")
|
||||
elif type(val) == list:
|
||||
exif[field] = [str(v).replace("\n", " ") for v in val if v is not None]
|
||||
return exif
|
||||
|
||||
|
||||
@@ -1640,6 +1710,9 @@ def _exiftool_json_sidecar(
|
||||
merge_exif_keywords=False,
|
||||
merge_exif_persons=False,
|
||||
filename=None,
|
||||
persons=True,
|
||||
location=True,
|
||||
replace_keywords=False,
|
||||
):
|
||||
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
||||
Does not include all the EXIF fields as those are likely already in the image.
|
||||
@@ -1654,13 +1727,18 @@ def _exiftool_json_sidecar(
|
||||
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||
filename: filename of the destination image file for including in exiftool signature in JSON sidecar
|
||||
persons: if True, include person data
|
||||
location: if True, include location data
|
||||
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
|
||||
|
||||
Returns: dict with exiftool tags / values
|
||||
|
||||
Exports the following:
|
||||
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)
|
||||
@@ -1688,6 +1766,9 @@ def _exiftool_json_sidecar(
|
||||
merge_exif_keywords=merge_exif_keywords,
|
||||
merge_exif_persons=merge_exif_persons,
|
||||
filename=filename,
|
||||
persons=persons,
|
||||
location=location,
|
||||
replace_keywords=replace_keywords,
|
||||
)
|
||||
|
||||
if not tag_groups:
|
||||
@@ -1710,6 +1791,9 @@ def _xmp_sidecar(
|
||||
extension=None,
|
||||
merge_exif_keywords=False,
|
||||
merge_exif_persons=False,
|
||||
persons=True,
|
||||
location=True,
|
||||
replace_keywords=False,
|
||||
):
|
||||
"""returns string for XMP sidecar
|
||||
use_albums_as_keywords: treat album names as keywords
|
||||
@@ -1719,6 +1803,9 @@ def _xmp_sidecar(
|
||||
extension: which extension to use for SidecarForExtension property
|
||||
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||
persons: if True, include person data
|
||||
location: if True, include location data
|
||||
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
|
||||
"""
|
||||
|
||||
xmp_template_file = (
|
||||
@@ -1742,22 +1829,23 @@ def _xmp_sidecar(
|
||||
if merge_exif_keywords:
|
||||
keyword_list.extend(self._get_exif_keywords())
|
||||
|
||||
if self.keywords:
|
||||
if self.keywords and not replace_keywords:
|
||||
keyword_list.extend(self.keywords)
|
||||
|
||||
# TODO: keyword handling in this and _exiftool_json_sidecar is
|
||||
# good candidate for pulling out in a function
|
||||
|
||||
person_list = []
|
||||
if merge_exif_persons:
|
||||
person_list.extend(self._get_exif_persons())
|
||||
if persons:
|
||||
if merge_exif_persons:
|
||||
person_list.extend(self._get_exif_persons())
|
||||
|
||||
if self.persons:
|
||||
# filter out _UNKNOWN_PERSON
|
||||
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||
if self.persons:
|
||||
# filter out _UNKNOWN_PERSON
|
||||
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||
|
||||
if use_persons_as_keywords and person_list:
|
||||
keyword_list.extend(person_list)
|
||||
if use_persons_as_keywords and person_list:
|
||||
keyword_list.extend(person_list)
|
||||
|
||||
if use_albums_as_keywords and self.albums:
|
||||
keyword_list.extend(self.albums)
|
||||
@@ -1787,11 +1875,14 @@ def _xmp_sidecar(
|
||||
# sorted mainly to make testing the XMP file easier
|
||||
if keyword_list:
|
||||
keyword_list = sorted(list(set(keyword_list)))
|
||||
if person_list:
|
||||
if persons and person_list:
|
||||
person_list = sorted(list(set(person_list)))
|
||||
|
||||
subject_list = keyword_list
|
||||
|
||||
if location:
|
||||
latlon = self.location
|
||||
|
||||
xmp_str = xmp_template.render(
|
||||
photo=self,
|
||||
description=description,
|
||||
@@ -1799,6 +1890,7 @@ def _xmp_sidecar(
|
||||
persons=person_list,
|
||||
subjects=subject_list,
|
||||
extension=extension,
|
||||
location=latlon,
|
||||
version=__version__,
|
||||
)
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from .._constants import (
|
||||
_PHOTOS_5_SHARED_PHOTO_PATH,
|
||||
_PHOTOS_5_VERSION,
|
||||
)
|
||||
from ..adjustmentsinfo import AdjustmentsInfo
|
||||
from ..albuminfo import AlbumInfo, ImportInfo
|
||||
from ..personinfo import FaceInfo, PersonInfo
|
||||
from ..phototemplate import PhotoTemplate
|
||||
@@ -452,9 +453,24 @@ class PhotoInfo:
|
||||
)
|
||||
return self._albums
|
||||
|
||||
@property
|
||||
def burst_albums(self):
|
||||
"""If photo is non-selected burst photo, list of albums any other images in the same burst set are contained in, otherwise returns self.albums"""
|
||||
if self.burst_selected or not self.burst:
|
||||
return self.albums
|
||||
|
||||
try:
|
||||
return self._burst_albums
|
||||
except AttributeError:
|
||||
burst_albums = []
|
||||
for photo in self.burst_photos:
|
||||
burst_albums.extend(photo.albums)
|
||||
self._burst_albums = list(set(burst_albums))
|
||||
return self._burst_albums
|
||||
|
||||
@property
|
||||
def album_info(self):
|
||||
""" list of AlbumInfo objects representing albums the photos is contained in """
|
||||
""" list of AlbumInfo objects representing albums the photo is contained in """
|
||||
try:
|
||||
return self._album_info
|
||||
except AttributeError:
|
||||
@@ -464,6 +480,21 @@ class PhotoInfo:
|
||||
]
|
||||
return self._album_info
|
||||
|
||||
@property
|
||||
def burst_album_info(self):
|
||||
""" If photo is a non-selected burst photo, returns list of AlbumInfo objects representing albums any other photos in the same burst set are contained in, otherwise returns self.album_info """
|
||||
if self.burst_selected or not self.burst:
|
||||
return self.album_info
|
||||
|
||||
try:
|
||||
return self._burst_album_info
|
||||
except AttributeError:
|
||||
burst_album_info = []
|
||||
for photo in self.burst_photos:
|
||||
burst_album_info.extend(photo.album_info)
|
||||
self._burst_album_info = list(set(burst_album_info))
|
||||
return self._burst_album_info
|
||||
|
||||
@property
|
||||
def import_info(self):
|
||||
""" ImportInfo object representing import session for the photo or None if no import session """
|
||||
@@ -510,6 +541,30 @@ class PhotoInfo:
|
||||
""" True if picture has adjustments / edits """
|
||||
return self._info["hasAdjustments"] == 1
|
||||
|
||||
@property
|
||||
def adjustments(self):
|
||||
""" Returns AdjustmentsInfo class for adjustment data or None if no adjustments; Photos 5+ only """
|
||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||
return None
|
||||
|
||||
if self.hasadjustments:
|
||||
try:
|
||||
return self._adjustmentinfo
|
||||
except AttributeError:
|
||||
library = self._db._library_path
|
||||
directory = self._uuid[0] # first char of uuid
|
||||
plist_file = (
|
||||
pathlib.Path(library)
|
||||
/ "resources"
|
||||
/ "renders"
|
||||
/ directory
|
||||
/ f"{self._uuid}.plist"
|
||||
)
|
||||
if not plist_file.is_file():
|
||||
return None
|
||||
self._adjustmentinfo = AdjustmentsInfo(plist_file)
|
||||
return self._adjustmentinfo
|
||||
|
||||
@property
|
||||
def external_edit(self):
|
||||
""" Returns True if picture was edited outside of Photos using external editor """
|
||||
@@ -655,6 +710,11 @@ class PhotoInfo:
|
||||
""" Returns True if photo is part of a Burst photo set, otherwise False """
|
||||
return self._info["burst"]
|
||||
|
||||
@property
|
||||
def burst_selected(self):
|
||||
""" Returns True if photo is a burst photo and has been selected from the burst set by the user, otherwise False """
|
||||
return self._info["burst_key"]
|
||||
|
||||
@property
|
||||
def burst_photos(self):
|
||||
"""If photo is a burst photo, returns list of PhotoInfo objects
|
||||
@@ -823,8 +883,19 @@ class PhotoInfo:
|
||||
|
||||
@property
|
||||
def orientation(self):
|
||||
""" returns EXIF orientation of the current photo version as int """
|
||||
return self._info["orientation"]
|
||||
""" returns EXIF orientation of the current photo version as int or 0 if current orientation cannot be determined """
|
||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||
return self._info["orientation"]
|
||||
|
||||
# For Photos 5+, try to get the adjusted orientation
|
||||
if self.hasadjustments:
|
||||
if self.adjustments:
|
||||
return self.adjustments.adj_orientation
|
||||
else:
|
||||
# can't reliably determine orientation for edited photo if adjustmentinfo not available
|
||||
return 0
|
||||
else:
|
||||
return self._info["orientation"]
|
||||
|
||||
@property
|
||||
def original_height(self):
|
||||
|
||||
@@ -1828,7 +1828,6 @@ class PhotosDB:
|
||||
|
||||
# get details about photos
|
||||
verbose("Processing photo details.")
|
||||
logging.debug(f"Getting information about photos")
|
||||
c.execute(
|
||||
f"""SELECT {asset_table}.ZUUID,
|
||||
ZADDITIONALASSETATTRIBUTES.ZMASTERFINGERPRINT,
|
||||
@@ -2041,6 +2040,7 @@ class PhotosDB:
|
||||
# > 6 = portrait (sometimes, see ZDEPTHSTATE/ZDEPTHTYPE)
|
||||
info["customRenderedValue"] = row[22]
|
||||
info["hdr"] = True if row[22] == 3 else False
|
||||
info["depth_state"] = row[36]
|
||||
info["portrait"] = True if row[36] != 0 else False
|
||||
|
||||
# Set panorama from either KindSubType or RenderedValue
|
||||
@@ -2735,8 +2735,6 @@ class PhotosDB:
|
||||
# an empty album will be in _dbalbum_titles but not _dbalbums_album
|
||||
pass
|
||||
album_set.update(title_set)
|
||||
else:
|
||||
logging.debug(f"Could not find album '{album}' in database")
|
||||
photos_sets.append(album_set)
|
||||
|
||||
if uuid:
|
||||
@@ -2744,8 +2742,6 @@ class PhotosDB:
|
||||
for u in uuid:
|
||||
if u in self._dbphotos:
|
||||
uuid_set.update([u])
|
||||
else:
|
||||
logging.debug(f"Could not find uuid '{u}' in database")
|
||||
photos_sets.append(uuid_set)
|
||||
|
||||
if keywords:
|
||||
@@ -2753,8 +2749,6 @@ class PhotosDB:
|
||||
for keyword in keywords:
|
||||
if keyword in self._dbkeywords_keyword:
|
||||
keyword_set.update(self._dbkeywords_keyword[keyword])
|
||||
else:
|
||||
logging.debug(f"Could not find keyword '{keyword}' in database")
|
||||
photos_sets.append(keyword_set)
|
||||
|
||||
if persons:
|
||||
@@ -2767,8 +2761,6 @@ class PhotosDB:
|
||||
except KeyError:
|
||||
# some persons have zero photos so they won't be in _dbfaces_pk
|
||||
pass
|
||||
else:
|
||||
logging.debug(f"Could not find person '{person}' in database")
|
||||
photos_sets.append(person_set)
|
||||
|
||||
if from_date or to_date: # sourcery off
|
||||
@@ -2779,14 +2771,10 @@ class PhotosDB:
|
||||
dsel = {
|
||||
k: v for k, v in dsel.items() if v["imageDate"] >= from_date
|
||||
}
|
||||
logging.debug(
|
||||
f"Found %i items with from_date {from_date}" % len(dsel)
|
||||
)
|
||||
if to_date:
|
||||
if not datetime_has_tz(to_date):
|
||||
to_date = datetime_naive_to_local(to_date)
|
||||
dsel = {k: v for k, v in dsel.items() if v["imageDate"] <= to_date}
|
||||
logging.debug(f"Found %i items with to_date {to_date}" % len(dsel))
|
||||
photos_sets.append(set(dsel.keys()))
|
||||
|
||||
photoinfo = []
|
||||
|
||||
94
osxphotos/phototemplate.md
Normal file
@@ -0,0 +1,94 @@
|
||||
The templating system converts one or template statements, written in osxphotos templating language, to one or more rendered values using information from the photo being processed.
|
||||
|
||||
In its simplest form, a template statement has the form: `"{template_field}"`, for example `"{title}"` which would resolve to the title of the photo.
|
||||
|
||||
Template statements may contain one or more modifiers. The full syntax is:
|
||||
|
||||
`"pretext{delim+template_field:subfield|filter(path_sep)[find,replace]?bool_value,default}posttext"`
|
||||
|
||||
Template statements are white-space sensitive meaning that white space (spaces, tabs) changes the meaning of the template statement.
|
||||
|
||||
`pretext` and `posttext` are free form text. For example, if a photo has title "My Photo Title". the template statement `"The title of the photo is {title}"`, resolves to `"The title of the photo is My Photo Title"`. The `pretext` in this example is `"The title if the photo is "` and the template_field is `{title}`.
|
||||
|
||||
|
||||
`delim`: optional delimiter string to use when expanding multi-valued template values in-place
|
||||
|
||||
`+`: If present before template `name`, expands the template in place. If `delim` not provided, values are joined with no delimiter.
|
||||
|
||||
e.g. if Photo keywords are `["foo","bar"]`:
|
||||
|
||||
- `"{keyword}"` renders to `"foo", "bar"`
|
||||
- `"{,+keyword}"` renders to: `"foo,bar"`
|
||||
- `"{; +keyword}"` renders to: `"foo; bar"`
|
||||
- `"{+keyword}"` renders to `"foobar"`
|
||||
|
||||
`template_field`: The template field to resolve. See [Template Substitutions](#template-substitutions) for full list of template fields.
|
||||
|
||||
`:subfield`: Some templates have sub-fields, For example, `{exiftool:IPTC:Make}`; the template_field is `exiftool` and the sub-field is `IPTC:Make`.
|
||||
|
||||
`|filter`: You may optionally append one or more filter commands to the end of the template field using the vertical pipe ('|') symbol. Filters may be combined, separated by '|' as in: `{keyword|capitalize|parens}`.
|
||||
|
||||
Valid filters are:
|
||||
|
||||
<!-- OSXPHOTOS-FILTER-TABLE:START - Do not remove or modify this section -->
|
||||
- lower: Convert value to lower case, e.g. 'Value' => 'value'.
|
||||
- upper: Convert value to upper case, e.g. 'Value' => 'VALUE'.
|
||||
- strip: Strip whitespace from beginning/end of value, e.g. ' Value ' => 'Value'.
|
||||
- titlecase: Convert value to title case, e.g. 'my value' => 'My Value'.
|
||||
- capitalize: Capitalize first word of value and convert other words to lower case, e.g. 'MY VALUE' => 'My value'.
|
||||
- braces: Enclose value in curly braces, e.g. 'value => '{value}'.
|
||||
- parens: Enclose value in parentheses, e.g. 'value' => '(value')
|
||||
- brackets: Enclose value in brackets, e.g. 'value' => '[value]'
|
||||
<!-- OSXPHOTOS-FILTER-TABLE:END -->
|
||||
|
||||
e.g. if Photo keywords are `["FOO","bar"]`:
|
||||
|
||||
- `"{keyword|lower}"` renders to `"foo", "bar"`
|
||||
- `"{keyword|upper}"` renders to: `"FOO", "BAR"`
|
||||
- `"{keyword|capitalize}"` renders to: `"Foo", "Bar"`
|
||||
- `"{keyword|lower|parens}"` renders to: `"(foo)", "(bar)"`
|
||||
|
||||
e.g. if Photo description is "my description":
|
||||
|
||||
- `"{descr|titlecase}"` renders to: `"My Description"`
|
||||
|
||||
`(path_sep)`: optional path separator to use when joining path-like fields, for example `{folder_album}`. Default is "/".
|
||||
|
||||
e.g. If Photo is in `Album1` in `Folder1`:
|
||||
|
||||
- `"{folder_album}"` renders to `["Folder1/Album1"]`
|
||||
- `"{folder_album(>)}"` renders to `["Folder1>Album1"]`
|
||||
- `"{folder_album()}"` renders to `["Folder1Album1"]`
|
||||
|
||||
`[find|replace]`: optional text replacement to perform on rendered template value. For example, to replace "/" in an album name, you could use the template `"{album[/,-]}"`. Multiple replacements can be made by appending "|" and adding another find|replace pair. e.g. to replace both "/" and ":" in album name: `"{album[/,-|:,-]}"`. find/replace pairs are not limited to single characters. The "|" character cannot be used in a find/replace pair.
|
||||
|
||||
`?bool_value`: Template fields may be evaluated as boolean by appending "?" after the field name (and following "(path_sep)" or "[find/replace]". If a field is True (e.g. photo is HDR and field is `"{hdr}"`) or has any value, the value following the "?" will be used to render the template instead of the actual field value. If the template field evaluates to False (e.g. in above example, photo is not HDR) or has no value (e.g. photo has no title and field is `"{title}"`) then the default value following a "," will be used.
|
||||
|
||||
e.g. if photo is an HDR image,
|
||||
|
||||
- `"{hdr?ISHDR,NOTHDR}"` renders to `"ISHDR"`
|
||||
|
||||
and if it is not an HDR image,
|
||||
|
||||
- `"{hdr?ISHDR,NOTHDR}"` renders to `"NOTHDR"`
|
||||
|
||||
`,default`: optional default value to use if the template name has no value. This modifier is also used for the value if False for boolean-type fields (see above) as well as to hold a sub-template for values like `{created.strftime}`. If no default value provided, "_" is used.
|
||||
|
||||
e.g., if photo has no title set,
|
||||
|
||||
- `"{title}"` renders to "_"
|
||||
- `"{title,I have no title}"` renders to `"I have no title"`
|
||||
|
||||
Template fields such as `created.strftime` use the default value to pass the template to use for `strftime`.
|
||||
|
||||
e.g., if photo date is 4 February 2020, 19:07:38,
|
||||
|
||||
- `"{created.strftime,%Y-%m-%d-%H%M%S}"` renders to `"2020-02-04-190738"`
|
||||
|
||||
Some template fields such as `"{media_type}"` use the default value to allow customization of the output. For example, `"{media_type}"` resolves to the special media type of the photo such as `panorama` or `selfie`. You may use the default value to override these in form: `"{media_type,video=vidéo;time_lapse=vidéo_accélérée}"`. In this example, if photo was a time_lapse photo, `media_type` would resolve to `vidéo_accélérée` instead of `time_lapse`.
|
||||
|
||||
Either or both bool_value or default (False value) may be empty which would result in empty string `""` when rendered.
|
||||
|
||||
If you want to include "{" or "}" in the output, use "{openbrace}" or "{closebrace}" template substitution.
|
||||
|
||||
e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{Photo Title}"`.
|
||||
122
osxphotos/phototemplate.tx
Normal file
@@ -0,0 +1,122 @@
|
||||
// OSXPhotos Template Language (OTL)
|
||||
// a TemplateString has format:
|
||||
// pre{delim+template_field:subfield|filter(path_sep)[find,replace]?bool_value,default}post
|
||||
// a TemplateStatement may contain zero or more TemplateStrings
|
||||
// The pre and post are optional strings
|
||||
// The template itself (inside the {}) is also optional but if present
|
||||
// everything but template_field is also optional
|
||||
|
||||
Statement:
|
||||
(template_strings+=TemplateString)?
|
||||
;
|
||||
|
||||
TemplateString:
|
||||
pre=NON_TEMPLATE_STRING?
|
||||
template=Template?
|
||||
post=NON_TEMPLATE_STRING?
|
||||
;
|
||||
|
||||
Template:
|
||||
(
|
||||
"{"
|
||||
delim=Delim
|
||||
field=Field
|
||||
subfield=SubField
|
||||
filter=Filter
|
||||
pathsep=PathSep
|
||||
findreplace=FindReplace
|
||||
bool=Boolean
|
||||
default=Default
|
||||
"}"
|
||||
)?
|
||||
;
|
||||
|
||||
NON_TEMPLATE_STRING:
|
||||
/[^\{\},]*/
|
||||
;
|
||||
|
||||
Delim:
|
||||
(
|
||||
(value=DELIM_WORD)?
|
||||
'+'
|
||||
)?
|
||||
;
|
||||
|
||||
DELIM_WORD:
|
||||
/[^\{\}]*(?=\+\w)/
|
||||
;
|
||||
|
||||
Field:
|
||||
FIELD_WORD+
|
||||
;
|
||||
|
||||
SubField:
|
||||
(
|
||||
":"-
|
||||
SUBFIELD_WORD+
|
||||
)?
|
||||
;
|
||||
|
||||
FIELD_WORD:
|
||||
/[\.\w]+/
|
||||
;
|
||||
|
||||
SUBFIELD_WORD:
|
||||
/[\.\w:]+/
|
||||
;
|
||||
|
||||
Filter:
|
||||
(
|
||||
"|"-
|
||||
(value+=FILTER_WORD['|'])?
|
||||
)?
|
||||
;
|
||||
|
||||
FILTER_WORD:
|
||||
/[\.\w]+/
|
||||
;
|
||||
|
||||
PathSep:
|
||||
(
|
||||
"("
|
||||
(value=/[^\(\)\{\}]{0,1}/)?
|
||||
")"
|
||||
)?
|
||||
;
|
||||
|
||||
FindReplace:
|
||||
(
|
||||
"["
|
||||
(pairs+=FindReplacePair['|'])?
|
||||
"]"
|
||||
)?
|
||||
;
|
||||
|
||||
FindReplacePair:
|
||||
find=FIND_WORD
|
||||
","
|
||||
(replace=REPLACE_WORD)?
|
||||
;
|
||||
|
||||
FIND_WORD:
|
||||
/[^\[\]\|]*(?=\,)/
|
||||
;
|
||||
|
||||
REPLACE_WORD:
|
||||
/[^\[\]\|]*/
|
||||
;
|
||||
|
||||
|
||||
Boolean:
|
||||
(
|
||||
"?"
|
||||
(value=Statement)?
|
||||
)?
|
||||
;
|
||||
|
||||
Default:
|
||||
(
|
||||
","
|
||||
(value=Statement)?
|
||||
)?
|
||||
;
|
||||
@@ -99,12 +99,6 @@
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="orientation(orientation)">
|
||||
% if orientation is not None:
|
||||
<tiff:Orientation>${orientation}</tiff:Orientation>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="mwg_face_regions(photo)">
|
||||
% if photo.face_info:
|
||||
<mwg-rs:Regions rdf:parseType="Resource">
|
||||
@@ -182,12 +176,7 @@
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
${gps_info(*photo.location)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
${orientation(photo.orientation)}
|
||||
${gps_info(*location)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
|
||||
@@ -99,12 +99,6 @@
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="orientation(orientation)">
|
||||
% if orientation is not None:
|
||||
<tiff:Orientation>${orientation}</tiff:Orientation>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="mwg_face_regions(photo)">
|
||||
% if photo.face_info:
|
||||
<mwg-rs:Regions rdf:parseType="Resource">
|
||||
@@ -182,12 +176,7 @@
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
${gps_info(*photo.location)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
${orientation(photo.orientation)}
|
||||
${gps_info(*location)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
|
||||
@@ -49,7 +49,7 @@ pathvalidate==2.2.1
|
||||
pexpect==4.8.0
|
||||
photoscript==0.1.0
|
||||
pickleshare==0.7.5
|
||||
Pillow==7.2.0
|
||||
Pillow==8.1.1
|
||||
pkginfo==1.5.0.1
|
||||
pluggy==0.12.0
|
||||
prompt-toolkit==3.0.4
|
||||
@@ -59,7 +59,7 @@ py==1.8.0
|
||||
py2app==0.21
|
||||
pycparser==2.20
|
||||
pyfiglet==0.8.post1
|
||||
Pygments==2.6.1
|
||||
Pygments==2.7.4
|
||||
PyInstaller==3.6
|
||||
pyinstaller-setuptools==2019.3
|
||||
pylint==2.3.1
|
||||
@@ -181,14 +181,16 @@ pyobjc-framework-Vision==6.2.2
|
||||
pyobjc-framework-WebKit==6.2.2
|
||||
pyparsing==2.4.1.1
|
||||
python-dateutil==2.8.1
|
||||
PyYAML==5.1.2
|
||||
PyYAML==5.4
|
||||
pyzmq==18.1.1
|
||||
readme-renderer==25.0
|
||||
regex==2020.2.20
|
||||
requests==2.23.0
|
||||
requests-toolbelt==0.9.1
|
||||
rich==9.11.1
|
||||
six==1.14.0
|
||||
termcolor==1.1.0
|
||||
textx==2.3.0
|
||||
toml==0.10.0
|
||||
tornado==6.0.4
|
||||
tox==3.19.0
|
||||
|
||||
2
setup.py
@@ -84,6 +84,8 @@ setup(
|
||||
"photoscript>=0.1.0",
|
||||
"toml>=0.10.0",
|
||||
"osxmetadata>=0.99.13",
|
||||
"textx==2.3.0",
|
||||
"rich>=9.11.1",
|
||||
],
|
||||
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]},
|
||||
include_package_data=True,
|
||||
|
||||
|
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 528 KiB |
@@ -7,7 +7,7 @@
|
||||
<key>hostuuid</key>
|
||||
<string>9575E48B-8D5F-5654-ABAC-4431B1167324</string>
|
||||
<key>pid</key>
|
||||
<integer>55247</integer>
|
||||
<integer>86501</integer>
|
||||
<key>processname</key>
|
||||
<string>photolibraryd</string>
|
||||
<key>uid</key>
|
||||
|
||||
|
Before Width: | Height: | Size: 577 KiB After Width: | Height: | Size: 577 KiB |
|
After Width: | Height: | Size: 2.6 MiB |
@@ -3,24 +3,24 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BackgroundHighlightCollection</key>
|
||||
<date>2020-12-16T05:41:43Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
<key>BackgroundHighlightEnrichment</key>
|
||||
<date>2020-12-16T05:41:42Z</date>
|
||||
<date>2021-03-13T16:38:24Z</date>
|
||||
<key>BackgroundJobAssetRevGeocode</key>
|
||||
<date>2020-12-16T05:41:43Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
<key>BackgroundJobSearch</key>
|
||||
<date>2020-12-16T05:41:43Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
<key>BackgroundPeopleSuggestion</key>
|
||||
<date>2020-12-16T05:41:41Z</date>
|
||||
<date>2021-03-13T16:38:23Z</date>
|
||||
<key>BackgroundUserBehaviorProcessor</key>
|
||||
<date>2020-12-16T05:41:43Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey</key>
|
||||
<date>2020-10-17T23:45:33Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key>
|
||||
<date>2020-10-17T23:45:24Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key>
|
||||
<date>2020-12-16T05:41:44Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
<key>SiriPortraitDonation</key>
|
||||
<date>2020-12-16T05:41:43Z</date>
|
||||
<date>2021-03-13T16:38:25Z</date>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 46 KiB |
56
tests/photoinfo_mock.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Selectively mock a PhotoInfo object"""
|
||||
|
||||
from osxphotos import PhotoInfo
|
||||
|
||||
|
||||
class PhotoInfoMock(PhotoInfo):
|
||||
def __init__(self, photo, **kwargs):
|
||||
self._photo = photo
|
||||
self._db = photo._db
|
||||
self._info = photo._info
|
||||
|
||||
for kw in kwargs:
|
||||
if hasattr(photo, kw):
|
||||
setattr(self, f"_mock_{kw}", kwargs[kw])
|
||||
else:
|
||||
raise ValueError(f"Not a PhotoInfo attribute: {kw}")
|
||||
|
||||
@property
|
||||
def hdr(self):
|
||||
return (
|
||||
self._mock_hdr
|
||||
if getattr(self, "_mock_hdr", None) is not None
|
||||
else self._photo.hdr
|
||||
)
|
||||
|
||||
@property
|
||||
def favorite(self):
|
||||
return (
|
||||
self._mock_favorite
|
||||
if getattr(self, "_mock_favorite", None) is not None
|
||||
else self._photo.favorite
|
||||
)
|
||||
|
||||
@property
|
||||
def hasadjustments(self):
|
||||
return (
|
||||
self._mock_hasadjustments
|
||||
if getattr(self, "_mock_hasadjustments", None) is not None
|
||||
else self._photo.hasadjustments
|
||||
)
|
||||
|
||||
@property
|
||||
def keywords(self):
|
||||
return (
|
||||
self._mock_keywords
|
||||
if getattr(self, "_mock_keywords", None) is not None
|
||||
else self._photo.keywords
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return (
|
||||
self._mock_title
|
||||
if getattr(self, "_mock_title", None) is not None
|
||||
else self._photo.title
|
||||
)
|
||||
@@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:Keywords": ["Kids"], "XMP:Subject": ["Kids"], "XMP:TagsList": ["Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids"], "XMP:Subject": ["Kids"], "XMP:TagsList": ["Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -46,10 +46,6 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:Keywords": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:Subject": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:TagsList": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:Subject": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:TagsList": ["AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -52,10 +52,6 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -46,10 +46,6 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:Keywords": ["Kids"], "XMP:Subject": ["Kids"], "XMP:TagsList": ["Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Kids"], "XMP:Subject": ["Kids"], "XMP:TagsList": ["Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:Subject": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:TagsList": ["Folder1/SubFolder2/AlbumInFolder", "Kids", "Pumpkin Farm", "Test Album (1)"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -54,10 +54,6 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"ImageDescription": "Girl holding pumpkin", "Description": "Girl holding pumpkin", "Title": "I found one!", "Keywords": ["Kids"], "Subject": ["Kids"], "TagsList": ["Kids"], "PersonInImage": ["Katie"], "DateTimeOriginal": "2018:09:28 16:07:07", "CreateDate": "2018:09:28 16:07:07", "OffsetTimeOriginal": "-04:00", "DateCreated": "2018:09:28", "TimeCreated": "16:07:07-04:00", "ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"ImageDescription": "Girl holding pumpkin", "Description": "Girl holding pumpkin", "Caption-Abstract": "Girl holding pumpkin", "Title": "I found one!", "ObjectName": "I found one!", "Keywords": ["Kids"], "Subject": ["Kids"], "TagsList": ["Kids"], "PersonInImage": ["Katie"], "DateTimeOriginal": "2018:09:28 16:07:07", "CreateDate": "2018:09:28 16:07:07", "OffsetTimeOriginal": "-04:00", "DateCreated": "2018:09:28", "TimeCreated": "16:07:07-04:00", "ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -1 +1 @@
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:Keywords": ["Katie", "Kids"], "XMP:Subject": ["Katie", "Kids"], "XMP:TagsList": ["Katie", "Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
[{"EXIF:ImageDescription": "Girl holding pumpkin", "XMP:Description": "Girl holding pumpkin", "IPTC:Caption-Abstract": "Girl holding pumpkin", "XMP:Title": "I found one!", "IPTC:ObjectName": "I found one!", "IPTC:Keywords": ["Katie", "Kids"], "XMP:Subject": ["Katie", "Kids"], "XMP:TagsList": ["Katie", "Kids"], "XMP:PersonInImage": ["Katie"], "EXIF:DateTimeOriginal": "2018:09:28 16:07:07", "EXIF:CreateDate": "2018:09:28 16:07:07", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:09:28", "IPTC:TimeCreated": "16:07:07-04:00", "EXIF:ModifyDate": "2018:09:28 16:07:07"}]
|
||||
@@ -48,10 +48,6 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"XMP:Title": "St. James's Park", "IPTC:Keywords": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:Subject": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:TagsList": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "EXIF:GPSLatitude": 51.50357167, "EXIF:GPSLongitude": -0.1318055, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:10:13 09:18:12", "EXIF:CreateDate": "2018:10:13 09:18:12", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:10:13", "IPTC:TimeCreated": "09:18:12-04:00", "EXIF:ModifyDate": "2019:12:01 11:43:45"}]
|
||||
[{"XMP:Title": "St. James's Park", "IPTC:ObjectName": "St. James's Park", "IPTC:Keywords": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:Subject": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:TagsList": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "EXIF:GPSLatitude": 51.50357167, "EXIF:GPSLongitude": -0.1318055, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:10:13 09:18:12", "EXIF:CreateDate": "2018:10:13 09:18:12", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:10:13", "IPTC:TimeCreated": "09:18:12-04:00", "EXIF:ModifyDate": "2019:12:01 11:43:45"}]
|
||||
@@ -53,10 +53,6 @@
|
||||
<exif:GPSLongitude>0,7.908329999999999W</exif:GPSLongitude>
|
||||
<exif:GPSLatitude>51,30.21430019999997N</exif:GPSLatitude>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:Orientation>1</tiff:Orientation>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"XMP:Title": "St. James's Park", "IPTC:Keywords": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:Subject": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:TagsList": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "EXIF:GPSLatitude": 51.50357167, "EXIF:GPSLongitude": -0.1318055, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:10:13 09:18:12", "EXIF:CreateDate": "2018:10:13 09:18:12", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:10:13", "IPTC:TimeCreated": "09:18:12-04:00", "EXIF:ModifyDate": "2019:12:01 11:43:45"}]
|
||||
[{"XMP:Title": "St. James's Park", "IPTC:ObjectName": "St. James's Park", "IPTC:Keywords": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:Subject": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "XMP:TagsList": ["England", "London", "London 2018", "St. James's Park", "UK", "United Kingdom"], "EXIF:GPSLatitude": 51.50357167, "EXIF:GPSLongitude": -0.1318055, "EXIF:GPSLatitudeRef": "N", "EXIF:GPSLongitudeRef": "W", "EXIF:DateTimeOriginal": "2018:10:13 09:18:12", "EXIF:CreateDate": "2018:10:13 09:18:12", "EXIF:OffsetTimeOriginal": "-04:00", "IPTC:DateCreated": "2018:10:13", "IPTC:TimeCreated": "09:18:12-04:00", "EXIF:ModifyDate": "2019:12:01 11:43:45"}]
|
||||