Compare commits

...

36 Commits

Author SHA1 Message Date
allcontributors[bot]
10ebe9e02b add pekingduck as a contributor for ideas (#1021)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-03-14 12:07:37 -07:00
Rhet Turnbull
6813e9f2b4 Release files for 0.58.2 (#1020) 2023-03-14 12:06:43 -07:00
Rhet Turnbull
1d1b69601f Added --replace-keywords to batch-edit #1018 (#1019) 2023-03-14 11:58:13 -07:00
Rhet Turnbull
3ad4c7a4cc Updated CHANGELOG.md [skip ci] 2023-03-09 06:59:08 -08:00
allcontributors[bot]
0dd628682f add ianmmoir as a contributor for bug (#1016)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-03-09 06:55:37 -08:00
Rhet Turnbull
8f7d88d8da Release files for 0.58.1 2023-03-09 06:50:23 -08:00
Rhet Turnbull
4fcd381262 Hot fix for null date, #1014 2023-03-09 06:49:05 -08:00
Rhet Turnbull
10d1ea8b2c rebuild pages 2023-03-09 06:32:58 -08:00
Rhet Turnbull
cfb623d19b Feature appends prepends 1010 (#1015)
* Added appends, prepends filters, #1010

* Fixed initialization of field_arg
2023-03-09 06:22:24 -08:00
Rhet Turnbull
a08680ed02 Added python and macOS versions to --version (#1008) 2023-02-28 06:43:06 -08:00
Rhet Turnbull
56ca54ad0d Updated CHANGELOG.md [skip ci] 2023-02-25 20:05:08 -08:00
allcontributors[bot]
2e4046c319 add eecue as a contributor for bug (#1005)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-02-25 14:56:09 -08:00
Rhet Turnbull
0b6be87d15 Release files for 0.58.0 (#1004) 2023-02-25 14:54:48 -08:00
Rhet Turnbull
b7c98aa548 Fixed docstring 2023-02-25 14:49:16 -08:00
Rhet Turnbull
0694662f78 Moved custom param types to param_types 2023-02-25 14:43:37 -08:00
Rhet Turnbull
7eb35b31ac Updated supported versions 2023-02-25 14:40:44 -08:00
Rhet Turnbull
94f484e9ec Feature date added 998 (#1003)
* Implemented --date-added, #998

* Added --date-added-from-photo

* Fixed typehint
2023-02-25 14:38:49 -08:00
Rhet Turnbull
0e1613f134 Fix error on closing export db, #999 (#1002) 2023-02-25 14:38:07 -08:00
Rhet Turnbull
1661cc9f0b Feature batch edit 949 (#1001)
* Initial implementation of batch-edit, #949

* Added tests for batch-edit, #949
2023-02-25 14:37:26 -08:00
Rhet Turnbull
1981340108 Added batch_edit.py example, [skip ci] 2023-02-23 06:43:41 -08:00
Rhet Turnbull
c3ca25fe33 Added batch_edit.py example, [skip ci] 2023-02-22 06:36:53 -08:00
Rhet Turnbull
1e10df26c9 Added batch_edit.py example, [skip ci] 2023-02-22 06:35:00 -08:00
Rhet Turnbull
8fdecd56f5 Added batch_edit.py example, [skip ci] 2023-02-21 21:29:59 -08:00
Rhet Turnbull
b2ed70b00c Added batch_edit.py example, [skip ci] 2023-02-21 21:26:07 -08:00
Rhet Turnbull
3a77a3e5f0 Added batch_edit.py example, [skip ci] 2023-02-21 21:16:53 -08:00
Rhet Turnbull
e89f8a14b4 Added batch_edit.py example, [skip ci] 2023-02-20 21:45:37 -08:00
Rhet Turnbull
82295513fe Added batch_edit.py example, [skip ci] 2023-02-20 21:43:37 -08:00
Rhet Turnbull
e7e3e72f75 Added batch_edit.py example, [skip ci] 2023-02-20 17:50:09 -08:00
Rhet Turnbull
9006708a30 Added batch_edit.py example, [skip ci] 2023-02-20 17:39:02 -08:00
Rhet Turnbull
4c2570a81d Added batch_edit.py example, [skip ci] 2023-02-20 17:18:38 -08:00
Rhet Turnbull
30e81eddb5 Updated CHANGELOG.md [skip ci] 2023-02-20 09:04:19 -08:00
Rhet Turnbull
5aa4a20bb5 Added pip caching 2023-02-20 08:58:08 -08:00
Rhet Turnbull
109917321a Updated docstrings, [skip-ci] 2023-02-20 08:54:44 -08:00
Rhet Turnbull
7fb7a551a8 Release files for 0.57.3 (#996) 2023-02-20 08:46:39 -08:00
Rhet Turnbull
d3267cb0f0 Added ability to show from export filepath to osxphotos show (#995) 2023-02-20 08:44:17 -08:00
Rhet Turnbull
1209d98e6e Updated CHANGELOG.md [skip ci] 2023-02-20 08:14:22 -08:00
69 changed files with 2159 additions and 444 deletions

View File

@@ -481,7 +481,8 @@
"profile": "http://eecue.com/",
"contributions": [
"ideas",
"userTesting"
"userTesting",
"bug"
]
},
{
@@ -512,6 +513,24 @@
"contributions": [
"ideas"
]
},
{
"login": "ianmmoir",
"name": "Ian Moir",
"avatar_url": "https://avatars.githubusercontent.com/u/15144745?v=4",
"profile": "http://www.projany.com",
"contributions": [
"bug"
]
},
{
"login": "pekingduck",
"name": "Peking Duck",
"avatar_url": "https://avatars.githubusercontent.com/u/2597142?v=4",
"profile": "https://github.com/pekingduck",
"contributions": [
"ideas"
]
}
],
"contributorsPerLine": 7,

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.57.2
current_version = 0.58.2
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
serialize = {major}.{minor}.{patch}

View File

@@ -18,6 +18,7 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip

View File

@@ -2170,6 +2170,8 @@ Valid filters are:
- `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
- `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
@@ -2371,7 +2373,7 @@ cog.out(get_template_field_table())
|{cr}|A carriage return: '\r'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.57.2'|
|{osxphotos_version}|The osxphotos version, e.g. '0.58.2'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@@ -2,6 +2,85 @@
All notable changes to this project will be documented in this file.
## [v0.58.1](https://github.com/RhetTbull/osxphotos/compare/v0.58.0...v0.58.1)
### 09 March 2023
#### Fixed
- Null times in Photos database (#1014)
#### Added
- Added appends, prepends filter to template system (#1015)
- Added python and macOS versions to --version output (#1008)
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
- [@ianmmoir](https://github.com/ianmmoir) for finding null times bug
## [v0.58.0](https://github.com/RhetTbull/osxphotos/compare/v0.57.3...v0.58.0)
### 25 February 2023
#### Added
- Added `osxphotos batch-edit` command to batch edit metadata (title, description, keywords, location) on selected photos. See `osxphotos help batch-edit` for more information. (#949)
- Added `--date-added` and `--date-added-from-photo` to `osxphotos timewarp` command to adjust the date added for selected photos. This is useful for removing photos from the Recents folder, for example. (#998)
#### Fixed
- Bug fix for export when retry failed to close export database. Thanks to [@eecue](https://github.com/eecue) for reporting this. (#999)
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
- [@eecue](https://github.com/eecue) for finding export database bug.
## [v0.57.3](https://github.com/RhetTbull/osxphotos/compare/v0.57.2...v0.57.3)
### 20 February 2023
### Added `osxphotos show FILEPATH`
#### Added
- `osxphotos show FILEPATH` to show a photo in Photos from the filepath to an exported photo, exported with `osxphotos export`
#### Internal
- Fixed a bug in `echo_error()` that occurred only in certain circumstances.
#### Contributors To This Release
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
## [v0.57.2](https://github.com/RhetTbull/osxphotos/compare/v0.57.1...v0.57.2)
### 20 February 2023
### Updated exportdb to add migrate library feature
#### Added
- `--migrate-photos-library` option added to `osxphotos exportdb` to migrate the export database from one Photos library to another. This is useful when moving to a new computer but maintaining the existing osxphotos export. Thanks to @swduncan for the idea. (#990)
#### Fixed
- Fixed a bug in `osxphotos export --cleanup` to handle files which could not be deleted. Thanks to @oPromessa for finding this and suggesting the fix. (#987)
#### Internal
- Fixed a bug that caused `rich_echo()` to not display rich text if `--verbose` wasn't specified.
#### Contributors To This Release
- [@RhetTbull](https://github.com/RhetTbull) for code changes.
- [@Promessa](https://github.com/promessa) who found the cleanup bug and suggested a code fix.
- [@swduncan](https://github.com/swduncan) who suggested the library migrate use case.
## [v0.57.1](https://github.com/RhetTbull/osxphotos/compare/v0.57.0...v0.57.1)
### 12 February 2023

View File

@@ -7,7 +7,7 @@
[![Downloads](https://static.pepy.tech/personalized-badge/osxphotos?period=month&units=international_system&left_color=black&right_color=brightgreen&left_text=downloads/month)](https://pepy.tech/project/osxphotos)
[![subreddit](https://img.shields.io/reddit/subreddit-subscribers/osxphotos?style=social)](https://www.reddit.com/r/osxphotos/)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-53-orange.svg?style=flat)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-55-orange.svg?style=flat)](#contributors)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
@@ -142,6 +142,7 @@ Commands:
about Print information about osxphotos including license.
add-locations Add missing location data to photos in Photos.app using...
albums Print out albums found in the Photos library.
batch-edit Batch edit photo metadata such as title, description,...
diff Compare two Photos databases and print out differences
docs Open osxphotos documentation in your browser.
dump Print list of all photos & associated info from the Photos...
@@ -1598,6 +1599,10 @@ Valid filters are:
['a', 'b', 'c', 'd'].
• prepend(x): Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c']
=> ['d', 'a', 'b', 'c'].
• appends(x): Append s[tring] Append x to each value of list of values, e.g.
appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
• prepends(x): Prepend s[tring] x to each value of list of values, e.g.
prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
• remove(x): Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] =>
['a', 'c'].
• slice(start:stop:step): Slice list using same semantics as Python's list
@@ -2089,7 +2094,7 @@ Substitution Description
{cr} A carriage return: '\r'
{crlf} A carriage return + line feed: '\r\n'
{tab} :A tab: '\t'
{osxphotos_version} The osxphotos version, e.g. '0.57.2'
{osxphotos_version} The osxphotos version, e.g. '0.58.2'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@@ -2376,6 +2381,8 @@ Valid filters are:
- `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
- `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
@@ -2574,7 +2581,7 @@ The following template field substitutions are availabe for use the templating s
|{cr}|A carriage return: '\r'|
|{crlf}|A carriage return + line feed: '\r\n'|
|{tab}|:A tab: '\t'|
|{osxphotos_version}|The osxphotos version, e.g. '0.57.2'|
|{osxphotos_version}|The osxphotos version, e.g. '0.58.2'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
@@ -2689,10 +2696,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://danielbeadle.net"><img src="https://avatars.githubusercontent.com/u/6235378?v=4?s=75" width="75px;" alt="Daniel Beadle"/><br /><sub><b>Daniel Beadle</b></sub></a><br /><a href="#ideas-djbeadle" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://eecue.com/"><img src="https://avatars.githubusercontent.com/u/532536?v=4?s=75" width="75px;" alt="Dave Bullock"/><br /><sub><b>Dave Bullock</b></sub></a><br /><a href="#ideas-eecue" title="Ideas, Planning, & Feedback">🤔</a> <a href="#userTesting-eecue" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://eecue.com/"><img src="https://avatars.githubusercontent.com/u/532536?v=4?s=75" width="75px;" alt="Dave Bullock"/><br /><sub><b>Dave Bullock</b></sub></a><br /><a href="#ideas-eecue" title="Ideas, Planning, & Feedback">🤔</a> <a href="#userTesting-eecue" title="User Testing">📓</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aeecue" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pweaver"><img src="https://avatars.githubusercontent.com/u/611620?v=4?s=75" width="75px;" alt="Pweaver"/><br /><sub><b>Pweaver</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Apweaver" title="Bug reports">🐛</a> <a href="#ideas-pweaver" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aa599"><img src="https://avatars.githubusercontent.com/u/37746269?v=4?s=75" width="75px;" alt="aa599"/><br /><sub><b>aa599</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aaa599" title="Bug reports">🐛</a> <a href="#ideas-aa599" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/swduncan"><img src="https://avatars.githubusercontent.com/u/2053195?v=4?s=75" width="75px;" alt="Steve Duncan"/><br /><sub><b>Steve Duncan</b></sub></a><br /><a href="#ideas-swduncan" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.projany.com"><img src="https://avatars.githubusercontent.com/u/15144745?v=4?s=75" width="75px;" alt="Ian Moir"/><br /><sub><b>Ian Moir</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aianmmoir" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pekingduck"><img src="https://avatars.githubusercontent.com/u/2597142?v=4?s=75" width="75px;" alt="Peking Duck"/><br /><sub><b>Peking Duck</b></sub></a><br /><a href="#ideas-pekingduck" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
</tbody>
</table>

View File

@@ -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: b087bae3b16ea7180756f2737542cb5a
config: dd34c3ea1cc9003033a129207a3fd912
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>Overview: module code - osxphotos 0.57.2 documentation</title>
<title>Overview: module code - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos._constants - osxphotos 0.57.1 documentation</title>
<title>osxphotos._constants - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.57.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -320,6 +320,7 @@
<span class="p">(</span><span class="s2">&quot;12&quot;</span><span class="p">,</span> <span class="s2">&quot;6&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;13&quot;</span><span class="p">,</span> <span class="s2">&quot;0&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;13&quot;</span><span class="p">,</span> <span class="s2">&quot;1&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;13&quot;</span><span class="p">,</span> <span class="s2">&quot;2&quot;</span><span class="p">),</span>
<span class="p">]</span>
<span class="c1"># Photos 5 has persons who are empty string if unidentified face</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.albuminfo - osxphotos 0.54.1 documentation</title>
<title>osxphotos.albuminfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.54.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.debug - osxphotos 0.56.7 documentation</title>
<title>osxphotos.debug - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.exiftool - osxphotos 0.56.4 documentation</title>
<title>osxphotos.exiftool - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.export_db - osxphotos 0.56.4 documentation</title>
<title>osxphotos.export_db - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -215,6 +215,7 @@
<span class="kn">from</span> <span class="nn">sqlite3</span> <span class="kn">import</span> <span class="n">Error</span>
<span class="kn">from</span> <span class="nn">tempfile</span> <span class="kn">import</span> <span class="n">TemporaryDirectory</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">from</span> <span class="nn">tenacity</span> <span class="kn">import</span> <span class="n">retry</span><span class="p">,</span> <span class="n">retry_if_not_exception_type</span><span class="p">,</span> <span class="n">stop_after_attempt</span>
@@ -239,6 +240,16 @@
<span class="n">MAX_EXPORT_RESULTS_DATA_ROWS</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;osxphotos&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">retry_log_error_no_raise</span><span class="p">(</span><span class="n">retry_state</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Log error for retry but don&#39;t raise exception&quot;&quot;&quot;</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Error </span><span class="si">{</span><span class="n">retry_state</span><span class="o">.</span><span class="n">outcome</span><span class="si">}</span><span class="s2"> for </span><span class="si">{</span><span class="n">retry_state</span><span class="o">.</span><span class="n">fn</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">(</span><span class="si">{</span><span class="n">retry_state</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="n">retry_state</span><span class="o">.</span><span class="n">kwargs</span><span class="si">}</span><span class="s2">); retrying...&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">pickle_and_zip</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Pickle and gzip data.</span>
@@ -300,7 +311,7 @@
<div class="viewcode-block" id="ExportDB.get_file_record"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_file_record">[docs]</a> <span class="nd">@retry</span><span class="p">(</span><span class="n">stop</span><span class="o">=</span><span class="n">stop_after_attempt</span><span class="p">(</span><span class="n">MAX_RETRY_ATTEMPTS</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">get_file_record</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">:</span> <span class="n">Union</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="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="s2">&quot;ExportRecord&quot;</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;get info for filename and uuid</span>
<span class="sd">&quot;&quot;&quot;get info for filename</span>
<span class="sd"> Returns: an ExportRecord object or None if filename not found</span>
<span class="sd"> &quot;&quot;&quot;</span>
@@ -1042,7 +1053,10 @@
<span class="n">conn_on_disk</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
<span class="n">conn_on_disk</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@retry</span><span class="p">(</span><span class="n">stop</span><span class="o">=</span><span class="n">stop_after_attempt</span><span class="p">(</span><span class="n">MAX_RETRY_ATTEMPTS</span><span class="p">))</span>
<span class="nd">@retry</span><span class="p">(</span>
<span class="n">stop</span><span class="o">=</span><span class="n">stop_after_attempt</span><span class="p">(</span><span class="n">MAX_RETRY_ATTEMPTS</span><span class="p">),</span>
<span class="n">retry_error_callback</span><span class="o">=</span><span class="n">retry_log_error_no_raise</span><span class="p">,</span> <span class="c1"># #999</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;close the database connection&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span><span class="p">:</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.fileutil - osxphotos 0.54.2 documentation</title>
<title>osxphotos.fileutil - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.54.2 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.54.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.personinfo - osxphotos 0.56.5 documentation</title>
<title>osxphotos.personinfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photoexporter - osxphotos 0.57.1 documentation</title>
<title>osxphotos.photoexporter - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.57.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photoinfo - osxphotos 0.57.2 documentation</title>
<title>osxphotos.photoinfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -1,13 +1,13 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosalbum - osxphotos 0.51.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosalbum - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -179,7 +179,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -194,7 +195,7 @@
</div>
<article role="main">
<h1>Source code for osxphotos.photosalbum</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosAlbum class to create an album in default Photos library and add photos to it """</span>
<span></span><span class="sd">&quot;&quot;&quot; PhotosAlbum class to create an album in default Photos library and add photos to it &quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
@@ -205,23 +206,23 @@
<span class="kn">from</span> <span class="nn">.photoinfo</span> <span class="kn">import</span> <span class="n">PhotoInfo</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"PhotosAlbum"</span><span class="p">,</span> <span class="s2">"PhotosAlbumPhotoScript"</span><span class="p">]</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;PhotosAlbum&quot;</span><span class="p">,</span> <span class="s2">&quot;PhotosAlbumPhotoScript&quot;</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">folder_by_path</span><span class="p">(</span><span class="n">folders</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Folder</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Folder by path (passed as list of folder names)"""</span>
<span class="sd">&quot;&quot;&quot;Get (and create if necessary) a Photos Folder by path (passed as list of folder names)&quot;&quot;&quot;</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="n">top_folder_name</span> <span class="o">=</span> <span class="n">folders</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">top_folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating folder &#39;</span><span class="si">{</span><span class="n">top_folder_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">top_folder</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">top_folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">top_folder</span>
<span class="k">for</span> <span class="n">folder_name</span> <span class="ow">in</span> <span class="n">folders</span><span class="p">:</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">folder</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating folder '</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating folder &#39;</span><span class="si">{</span><span class="n">folder_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">folder</span> <span class="o">=</span> <span class="n">current_folder</span><span class="o">.</span><span class="n">create_folder</span><span class="p">(</span><span class="n">folder_name</span><span class="p">)</span>
<span class="n">current_folder</span> <span class="o">=</span> <span class="n">folder</span>
<span class="k">return</span> <span class="n">current_folder</span>
@@ -230,7 +231,7 @@
<span class="k">def</span> <span class="nf">album_by_path</span><span class="p">(</span>
<span class="n">folders_album</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Album</span><span class="p">:</span>
<span class="sd">"""Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)"""</span>
<span class="sd">&quot;&quot;&quot;Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)&quot;&quot;&quot;</span>
<span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">folders_album</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
@@ -239,21 +240,21 @@
<span class="n">folder</span> <span class="o">=</span> <span class="n">folder_by_path</span><span class="p">(</span><span class="n">folders_album</span><span class="p">,</span> <span class="n">verbose</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating album &#39;</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">folder</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># only have album name</span>
<span class="n">album_name</span> <span class="o">=</span> <span class="n">folders_album</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">album_name</span><span class="p">,</span> <span class="n">top_level</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating album '</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating album &#39;</span><span class="si">{</span><span class="n">album_name</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">album_name</span><span class="p">)</span>
<span class="k">return</span> <span class="n">album</span>
<div class="viewcode-block" id="PhotosAlbum"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbum">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbum</span><span class="p">:</span>
<span class="sd">"""Add osxphotos.photoinfo.PhotoInfo objects to album"""</span>
<span class="sd">&quot;&quot;&quot;Add osxphotos.photoinfo.PhotoInfo objects to album&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -262,17 +263,17 @@
<span class="n">split_folder</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">rich</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">"""Return a PhotosAlbum object, creating the album if necessary</span>
<span class="sd">&quot;&quot;&quot;Return a PhotosAlbum object, creating the album if necessary</span>
<span class="sd"> Args:</span>
<span class="sd"> name: Name of album</span>
<span class="sd"> verbose: optional callable to print verbose output</span>
<span class="sd"> split_folder: if set, split album name on value of split_folder to create folders if necessary,</span>
<span class="sd"> e.g. if name = 'folder1/folder2/album' and split_folder='/',</span>
<span class="sd"> then folders 'folder1' and 'folder2' will be created and album 'album' will be created in 'folder2';</span>
<span class="sd"> if not set, album 'folder1/folder2/album' will be created</span>
<span class="sd"> e.g. if name = &#39;folder1/folder2/album&#39; and split_folder=&#39;/&#39;,</span>
<span class="sd"> then folders &#39;folder1&#39; and &#39;folder2&#39; will be created and album &#39;album&#39; will be created in &#39;folder2&#39;;</span>
<span class="sd"> if not set, album &#39;folder1/folder2/album&#39; will be created</span>
<span class="sd"> rich: if True, use rich themes for verbose output</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</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="ow">or</span> <span class="n">noop</span>
<span class="bp">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">PhotosLibrary</span><span class="p">()</span>
@@ -285,7 +286,7 @@
<span class="n">photo_</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo_</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]):</span>
@@ -295,42 +296,42 @@
<span class="n">photos</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Error creating Photo object for photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Error creating Photo object for photo </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photos</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">&#39;photo&#39;</span><span class="p">,</span> <span class="s1">&#39;photos&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">photos</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_format_uuid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uuid</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format uuid for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[uuid]</span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2">[/uuid]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">uuid</span>
<span class="sd">&quot;&quot;&quot; &quot;Format uuid for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[uuid]</span><span class="si">{</span><span class="n">uuid</span><span class="si">}</span><span class="s2">[/uuid]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">uuid</span>
<span class="k">def</span> <span class="nf">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">album</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format album name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filepath]</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">[/filepath]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">album</span>
<span class="sd">&quot;&quot;&quot; &quot;Format album name for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[filepath]</span><span class="si">{</span><span class="n">album</span><span class="si">}</span><span class="s2">[/filepath]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">album</span>
<span class="k">def</span> <span class="nf">_format_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format name for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[filename]</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">[/filename]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">name</span>
<span class="sd">&quot;&quot;&quot; &quot;Format name for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[filename]</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">[/filename]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="n">name</span>
<span class="k">def</span> <span class="nf">_format_num</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">num</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">""" "Format number for verbose output"""</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"[num]</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">[/num]"</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">num</span><span class="p">)</span></div>
<span class="sd">&quot;&quot;&quot; &quot;Format number for verbose output&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;[num]</span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">[/num]&quot;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">rich</span> <span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">num</span><span class="p">)</span></div>
<div class="viewcode-block" id="PhotosAlbumPhotoScript"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbumPhotoScript">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbumPhotoScript</span><span class="p">(</span><span class="n">PhotosAlbum</span><span class="p">):</span>
<span class="sd">"""Add photoscript.Photo objects to album"""</span>
<span class="sd">&quot;&quot;&quot;Add photoscript.Photo objects to album&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">Photo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_name</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_uuid</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Photo</span><span class="p">]):</span>
@@ -338,7 +339,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;Added </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_num</span><span class="p">(</span><span class="n">photo_len</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">&#39;photo&#39;</span><span class="p">,</span> <span class="s1">&#39;photos&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_album</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span></div>
</pre></div>
</article>
@@ -377,7 +378,9 @@
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>

View File

@@ -1,13 +1,13 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.50.13 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -179,7 +179,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -194,8 +195,8 @@
</div>
<article role="main">
<h1>Source code for osxphotos.photosdb._photosdb_process_comments</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly """</span>
<span></span><span class="sd">&quot;&quot;&quot; PhotosDB method for processing comments and likes on shared photos.</span>
<span class="sd"> Do not import this module directly &quot;&quot;&quot;</span>
<span class="kn">import</span> <span class="nn">dataclasses</span>
<span class="kn">import</span> <span class="nn">datetime</span>
@@ -207,10 +208,10 @@
<span class="k">def</span> <span class="nf">_process_comments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""load the comments and likes data from the database</span>
<span class="sd">&quot;&quot;&quot;load the comments and likes data from the database</span>
<span class="sd"> this is a PhotosDB method that should be imported in</span>
<span class="sd"> the PhotosDB class definition in photosdb.py</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_hashed_person_id</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db_version</span> <span class="o">&lt;=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
@@ -221,7 +222,7 @@
<div class="viewcode-block" id="CommentInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.CommentInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">CommentInfo</span><span class="p">:</span>
<span class="sd">"""Class for shared photo comments"""</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo comments&quot;&quot;&quot;</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -234,7 +235,7 @@
<div class="viewcode-block" id="LikeInfo"><a class="viewcode-back" href="../../../reference.html#osxphotos.LikeInfo">[docs]</a><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">LikeInfo</span><span class="p">:</span>
<span class="sd">"""Class for shared photo likes"""</span>
<span class="sd">&quot;&quot;&quot;Class for shared photo likes&quot;&quot;&quot;</span>
<span class="n">datetime</span><span class="p">:</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span>
<span class="n">user</span><span class="p">:</span> <span class="nb">str</span>
@@ -247,25 +248,25 @@
<span class="c1"># The following methods do not get imported into PhotosDB</span>
<span class="c1"># but will get called by _process_comments</span>
<span class="k">def</span> <span class="nf">_process_comments_4</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">"""process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &lt;= 4</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">."</span>
<span class="sa">f</span><span class="s2">&quot;Not implemented for database version </span><span class="si">{</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_db_version</span><span class="si">}</span><span class="s2">.&quot;</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_comments_5</span><span class="p">(</span><span class="n">photosdb</span><span class="p">):</span>
<span class="sd">"""process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance"""</span>
<span class="sd">&quot;&quot;&quot;process comments and likes info for Photos &gt;= 5</span>
<span class="sd"> photosdb: PhotosDB instance&quot;&quot;&quot;</span>
<span class="n">db</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_tmp_db</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">"ASSET"</span><span class="p">]</span>
<span class="n">asset_table</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photosdb</span><span class="o">.</span><span class="n">_photos_ver</span><span class="p">][</span><span class="s2">&quot;ASSET&quot;</span><span class="p">]</span>
<span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">=</span> <span class="n">sqlite_open_ro</span><span class="p">(</span><span class="n">db</span><span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sd">"""</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> SELECT DISTINCT</span>
<span class="sd"> ZINVITEEHASHEDPERSONID AS HASHEDPERSONID,</span>
<span class="sd"> ZINVITEEFIRSTNAME AS FIRSTNAME,</span>
@@ -273,7 +274,7 @@
<span class="sd"> ZINVITEEFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZCLOUDSHAREDALBUMINVITATIONRECORD</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> UNION</span>
<span class="sd"> SELECT DISTINCT</span>
@@ -283,9 +284,9 @@
<span class="sd"> ZCLOUDOWNERFULLNAME AS FULLNAME</span>
<span class="sd"> FROM ZGENERICALBUM</span>
<span class="sd"> WHERE HASHEDPERSONID IS NOT NULL</span>
<span class="sd"> AND HASHEDPERSONID != ""</span>
<span class="sd"> AND HASHEDPERSONID != &quot;&quot;</span>
<span class="sd"> AND NOT (FIRSTNAME IS NULL AND LASTNAME IS NULL)</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
@@ -298,35 +299,35 @@
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="o">.</span><span class="n">fetchall</span><span class="p">():</span>
<span class="n">person_id</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">person_id</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"first_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">"last_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">"full_name"</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="s2">&quot;first_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span>
<span class="s2">&quot;last_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span>
<span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span>
<span class="p">}</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"""</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> SELECT </span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID, -- UUID of the photo</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="s2"> ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="s2"> FROM ZCLOUDSHAREDCOMMENT</span>
<span class="s2"> JOIN </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"> ON</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZCOMMENTEDASSET</span>
<span class="s2"> OR</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK = ZCLOUDSHAREDCOMMENT.ZLIKEDASSET</span>
<span class="s2"> """</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="p">)</span>
<span class="c1"># order of results</span>
<span class="c1"># 0: ZGENERICASSET.ZUUID, -- UUID of the photo</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a "like"</span>
<span class="c1"># 1: ZCLOUDSHAREDCOMMENT.ZISLIKE, -- comment is actually a &quot;like&quot;</span>
<span class="c1"># 2: ZCLOUDSHAREDCOMMENT.ZCOMMENTDATE, -- date of comment</span>
<span class="c1"># 3: ZCLOUDSHAREDCOMMENT.ZCOMMENTTEXT, -- text of comment</span>
<span class="c1"># 4: ZCLOUDSHAREDCOMMENT.ZCOMMENTERHASHEDPERSONID, -- hashed ID of person who made comment/like</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user's) comment</span>
<span class="c1"># 5: ZCLOUDSHAREDCOMMENT.ZISMYCOMMENT -- is my (this user&#39;s) comment</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
@@ -334,7 +335,7 @@
<span class="n">is_like</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">normalize_unicode</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">"full_name"</span><span class="p">]</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_hashed_person_id</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]][</span><span class="s2">&quot;full_name&quot;</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">user_name</span> <span class="o">=</span> <span class="kc">None</span>
@@ -348,20 +349,20 @@
<span class="k">try</span><span class="p">:</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"likes"</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">"comments"</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;likes&quot;</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">&quot;comments&quot;</span><span class="p">:</span> <span class="p">[]}</span>
<span class="n">db_comments</span> <span class="o">=</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
<span class="k">if</span> <span class="n">is_like</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">LikeInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">text</span><span class="p">:</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="n">db_comments</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">CommentInfo</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">user_name</span><span class="p">,</span> <span class="n">ismine</span><span class="p">,</span> <span class="n">text</span><span class="p">))</span>
<span class="c1"># sort results</span>
<span class="k">for</span> <span class="n">uuid</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"likes"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">"comments"</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">"comments"</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]:</span>
<span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;likes&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="k">if</span> <span class="n">photosdb</span><span class="o">.</span><span class="n">_db_comments_uuid</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;comments&quot;</span><span class="p">]:</span>
<span class="n">value</span><span class="p">[</span><span class="s2">&quot;comments&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="n">datetime</span><span class="p">)</span>
<span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
@@ -401,7 +402,9 @@
</div><script data-url_root="../../../" id="documentation_options" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/sphinx_highlight.js"></script>
<script src="../../../_static/scripts/furo.js"></script>
<script src="../../../_static/clipboard.min.js"></script>
<script src="../../../_static/copybutton.js"></script>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.photosdb.photosdb - osxphotos 0.57.2 documentation</title>
<title>osxphotos.photosdb.photosdb - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -1239,9 +1239,8 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span>
<span class="n">row</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">+</span> <span class="n">TIME_DELTA</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes the date is invalid or null</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;lastmodifieddate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageTimeZoneOffsetSeconds&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span>
@@ -1253,8 +1252,8 @@
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 as image date</span>
<span class="n">imagedate</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</span><span class="p">[</span><span class="n">uuid</span><span class="p">][</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
@@ -2216,8 +2215,8 @@
<span class="n">delta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">seconds</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date (#1014)</span>
<span class="n">imagedate</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;imageDate&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">imagedate</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
@@ -2799,8 +2798,8 @@
<span class="c1"># save raw time stamp valu</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span> <span class="o">+</span> <span class="s2">&quot;_timestamp&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span><span class="p">]</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">moment_date</span><span class="o">.</span><span class="n">astimezone</span><span class="p">(</span><span class="n">tz</span><span class="o">=</span><span class="n">tz</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="c1"># sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date</span>
<span class="k">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">):</span>
<span class="c1"># sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date</span>
<span class="n">moment_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">1970</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">tz</span> <span class="o">=</span> <span class="n">timezone</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
<span class="n">moment_info</span><span class="p">[</span><span class="n">date_name</span> <span class="o">+</span> <span class="s2">&quot;_timestamp&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">date_stamp</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.phototemplate - osxphotos 0.57.1 documentation</title>
<title>osxphotos.phototemplate - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.57.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -482,6 +482,8 @@
<span class="o">+</span> <span class="s2">&quot;e.g. join(): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; &#39;abc&#39;.&quot;</span><span class="p">,</span>
<span class="s2">&quot;append(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append x to list of values, e.g. append(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepend(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend x to list of values, e.g. prepend(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;d&#39;, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;appends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Append s[tring] Append x to each value of list of values, e.g. appends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;ad&#39;, &#39;bd&#39;, &#39;cd&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepends(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Prepend s[tring] x to each value of list of values, e.g. prepends(d): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;da&#39;, &#39;db&#39;, &#39;dc&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;remove(x)&quot;</span><span class="p">:</span> <span class="s2">&quot;Remove x from list of values, e.g. remove(b): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] =&gt; [&#39;a&#39;, &#39;c&#39;].&quot;</span><span class="p">,</span>
<span class="s2">&quot;slice(start:stop:step)&quot;</span><span class="p">:</span> <span class="s2">&quot;Slice list using same semantics as Python&#39;s list slicing, &quot;</span>
<span class="o">+</span> <span class="s2">&quot;e.g. slice(1:3): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;c&#39;]; slice(1:4:2): [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;] =&gt; [&#39;b&#39;, &#39;d&#39;]; &quot;</span>
@@ -740,6 +742,8 @@
<span class="c1"># process field arguments</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">field_arg</span> <span class="o">=</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">fieldarg</span><span class="o">.</span><span class="n">value</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">field_arg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># process delim</span>
<span class="k">if</span> <span class="n">ts</span><span class="o">.</span><span class="n">template</span><span class="o">.</span><span class="n">delim</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
@@ -1204,15 +1208,17 @@
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unknown filter: </span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">filter_</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">&quot;split&quot;</span><span class="p">,</span>
<span class="s2">&quot;chop&quot;</span><span class="p">,</span>
<span class="s2">&quot;chomp&quot;</span><span class="p">,</span>
<span class="s2">&quot;append&quot;</span><span class="p">,</span>
<span class="s2">&quot;appends&quot;</span><span class="p">,</span>
<span class="s2">&quot;chomp&quot;</span><span class="p">,</span>
<span class="s2">&quot;chop&quot;</span><span class="p">,</span>
<span class="s2">&quot;filter&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepend&quot;</span><span class="p">,</span>
<span class="s2">&quot;prepends&quot;</span><span class="p">,</span>
<span class="s2">&quot;remove&quot;</span><span class="p">,</span>
<span class="s2">&quot;slice&quot;</span><span class="p">,</span>
<span class="s2">&quot;split&quot;</span><span class="p">,</span>
<span class="s2">&quot;sslice&quot;</span><span class="p">,</span>
<span class="s2">&quot;filter&quot;</span><span class="p">,</span>
<span class="p">]</span> <span class="ow">and</span> <span class="p">(</span><span class="n">args</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">filter_</span><span class="si">}</span><span class="s2"> requires arguments&quot;</span><span class="p">)</span>
@@ -1229,15 +1235,13 @@
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;braces&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;{&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;}&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;parens&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;(&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;(</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;brackets&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;[&quot;</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">&quot;]&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;[</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">]&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;shell_quote&quot;</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;split&quot;</span><span class="p">:</span>
<span class="c1"># split on delimiter</span>
<span class="n">delim</span> <span class="o">=</span> <span class="n">args</span>
<span class="k">if</span> <span class="n">delim</span><span class="p">:</span>
<span class="k">if</span> <span class="n">delim</span> <span class="o">:=</span> <span class="n">args</span><span class="p">:</span>
<span class="n">new_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
<span class="n">new_values</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">delim</span><span class="p">))</span>
@@ -1248,15 +1252,15 @@
<span class="c1"># chop off characters from the end</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chop</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chop: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chop: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[:</span><span class="o">-</span><span class="n">chop</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chop</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;chomp&quot;</span><span class="p">:</span>
<span class="c1"># chop off characters from the beginning</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">chomp</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chomp: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Invalid value for chomp: </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span><span class="p">[</span><span class="n">chomp</span><span class="p">:]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span> <span class="k">if</span> <span class="n">chomp</span> <span class="k">else</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;autosplit&quot;</span><span class="p">:</span>
<span class="c1"># try to split keyword strings automatically</span>
@@ -1291,6 +1295,12 @@
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepend&quot;</span><span class="p">:</span>
<span class="c1"># prepend value to list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">args</span><span class="p">]</span> <span class="o">+</span> <span class="n">values</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;appends&quot;</span><span class="p">:</span>
<span class="c1"># append value to each item in list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">v</span><span class="si">}{</span><span class="n">args</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;prepends&quot;</span><span class="p">:</span>
<span class="c1"># prepend value to each item in list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">args</span><span class="si">}{</span><span class="n">v</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">&quot;remove&quot;</span><span class="p">:</span>
<span class="c1"># remove value from list</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span> <span class="k">if</span> <span class="n">v</span> <span class="o">!=</span> <span class="n">args</span><span class="p">]</span>

View File

@@ -1,13 +1,13 @@
<!doctype html>
<html class="no-js">
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.placeinfo - osxphotos 0.51.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.placeinfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.51.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.51.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>
@@ -179,7 +179,8 @@
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
@@ -194,13 +195,13 @@
</div>
<article role="main">
<h1>Source code for osxphotos.placeinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">""" </span>
<span></span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> PlaceInfo class</span>
<span class="sd"> Provides reverse geolocation info for photos </span>
<span class="sd"> </span>
<span class="sd"> See https://developer.apple.com/documentation/corelocation/clplacemark</span>
<span class="sd"> for additional documentation on reverse geolocation data</span>
<span class="sd">"""</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
@@ -211,27 +212,27 @@
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"PLRevGeoLocationInfo"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItem"</span><span class="p">,</span>
<span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">,</span>
<span class="s2">"CNPostalAddress"</span><span class="p">,</span>
<span class="s2">"PlaceInfo"</span><span class="p">,</span>
<span class="s2">"PlaceInfo4"</span><span class="p">,</span>
<span class="s2">"PlaceInfo5"</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">,</span>
<span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo4&quot;</span><span class="p">,</span>
<span class="s2">&quot;PlaceInfo5&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="c1"># postal address information, returned by PlaceInfo.address</span>
<span class="n">PostalAddress</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">"PostalAddress"</span><span class="p">,</span>
<span class="s2">&quot;PostalAddress&quot;</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">"street"</span><span class="p">,</span>
<span class="s2">"sub_locality"</span><span class="p">,</span>
<span class="s2">"city"</span><span class="p">,</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span>
<span class="s2">"state_province"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span>
<span class="s2">"iso_country_code"</span><span class="p">,</span>
<span class="s2">&quot;street&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_locality&quot;</span><span class="p">,</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span>
<span class="s2">&quot;iso_country_code&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="p">)</span>
@@ -240,30 +241,30 @@
<span class="c1"># PLRevGeoLocationInfo.mapInfo.sortedPlaceInfos</span>
<span class="c1"># field 18 is combined bodies of water (ocean + inland_water)</span>
<span class="c1"># and maps to Photos &lt;= 4, RKPlace.type == 44</span>
<span class="c1"># (Photos &lt;= 4 doesn't have ocean or inland_water types)</span>
<span class="c1"># The fields named "field0", etc. appear to be unused</span>
<span class="c1"># (Photos &lt;= 4 doesn&#39;t have ocean or inland_water types)</span>
<span class="c1"># The fields named &quot;field0&quot;, etc. appear to be unused</span>
<span class="n">PlaceNames</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span>
<span class="s2">"PlaceNames"</span><span class="p">,</span>
<span class="s2">&quot;PlaceNames&quot;</span><span class="p">,</span>
<span class="p">[</span>
<span class="s2">"field0"</span><span class="p">,</span>
<span class="s2">"country"</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">"state_province"</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">"sub_administrative_area"</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">"city"</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">"field5"</span><span class="p">,</span>
<span class="s2">"additional_city_info"</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">"ocean"</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">"area_of_interest"</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">"inland_water"</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">"field10"</span><span class="p">,</span>
<span class="s2">"region"</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">"sub_throughfare"</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">"field13"</span><span class="p">,</span>
<span class="s2">"postal_code"</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">"field15"</span><span class="p">,</span>
<span class="s2">"field16"</span><span class="p">,</span>
<span class="s2">"street_address"</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">"body_of_water"</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="s2">&quot;field0&quot;</span><span class="p">,</span>
<span class="s2">&quot;country&quot;</span><span class="p">,</span> <span class="c1"># The name of the country associated with the placemark.</span>
<span class="s2">&quot;state_province&quot;</span><span class="p">,</span> <span class="c1"># administrativeArea, The state or province associated with the placemark.</span>
<span class="s2">&quot;sub_administrative_area&quot;</span><span class="p">,</span> <span class="c1"># Additional administrative area information for the placemark.</span>
<span class="s2">&quot;city&quot;</span><span class="p">,</span> <span class="c1"># locality, The city associated with the placemark.</span>
<span class="s2">&quot;field5&quot;</span><span class="p">,</span>
<span class="s2">&quot;additional_city_info&quot;</span><span class="p">,</span> <span class="c1"># subLocality, Additional city-level information for the placemark.</span>
<span class="s2">&quot;ocean&quot;</span><span class="p">,</span> <span class="c1"># The name of the ocean associated with the placemark.</span>
<span class="s2">&quot;area_of_interest&quot;</span><span class="p">,</span> <span class="c1"># areasOfInterest, The relevant areas of interest associated with the placemark.</span>
<span class="s2">&quot;inland_water&quot;</span><span class="p">,</span> <span class="c1"># The name of the inland water body associated with the placemark.</span>
<span class="s2">&quot;field10&quot;</span><span class="p">,</span>
<span class="s2">&quot;region&quot;</span><span class="p">,</span> <span class="c1"># The geographic region associated with the placemark.</span>
<span class="s2">&quot;sub_throughfare&quot;</span><span class="p">,</span> <span class="c1"># Additional street-level information for the placemark.</span>
<span class="s2">&quot;field13&quot;</span><span class="p">,</span>
<span class="s2">&quot;postal_code&quot;</span><span class="p">,</span> <span class="c1"># The postal code associated with the placemark.</span>
<span class="s2">&quot;field15&quot;</span><span class="p">,</span>
<span class="s2">&quot;field16&quot;</span><span class="p">,</span>
<span class="s2">&quot;street_address&quot;</span><span class="p">,</span> <span class="c1"># throughfare, The street address associated with the placemark.</span>
<span class="s2">&quot;body_of_water&quot;</span><span class="p">,</span> <span class="c1"># RKPlace.type == 44, appears to be any body of water (ocean or inland)</span>
<span class="p">],</span>
<span class="p">)</span>
@@ -271,7 +272,7 @@
<span class="c1"># in ZADDITIONALASSETATTRIBUTES.ZREVERSELOCATIONDATA</span>
<span class="c1"># These classes are used by bpylist.archiver to unarchive the serialized objects</span>
<span class="k">class</span> <span class="nc">PLRevGeoLocationInfo</span><span class="p">:</span>
<span class="sd">"""The top level reverse geolocation object"""</span>
<span class="sd">&quot;&quot;&quot;The top level reverse geolocation object&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -299,14 +300,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">"addressString"</span><span class="p">,</span>
<span class="s2">"countryCode"</span><span class="p">,</span>
<span class="s2">"isHome"</span><span class="p">,</span>
<span class="s2">"compoundNames"</span><span class="p">,</span>
<span class="s2">"compoundSecondaryNames"</span><span class="p">,</span>
<span class="s2">"version"</span><span class="p">,</span>
<span class="s2">"geoServiceProvider"</span><span class="p">,</span>
<span class="s2">"postalAddress"</span><span class="p">,</span>
<span class="s2">&quot;addressString&quot;</span><span class="p">,</span>
<span class="s2">&quot;countryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;isHome&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span>
<span class="s2">&quot;version&quot;</span><span class="p">,</span>
<span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span>
<span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -314,31 +315,31 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">"</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;addressString: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">addressString</span><span class="si">}</span><span class="s2">, countryCode: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">countryCode</span><span class="si">}</span><span class="s2">, isHome: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">isHome</span><span class="si">}</span><span class="s2">, mapItem: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">mapItem</span><span class="si">}</span><span class="s2">, postalAddress: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">postalAddress</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">addressString</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">countryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">mapItem</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">isHome</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">compoundSecondaryNames</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">version</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">geoServiceProvider</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">postalAddress</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"addressString"</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"countryCode"</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"mapItem"</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"isHome"</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundNames"</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"compoundSecondaryNames"</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"version"</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"geoServiceProvider"</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"postalAddress"</span><span class="p">)</span>
<span class="n">addressString</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;addressString&quot;</span><span class="p">)</span>
<span class="n">countryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;countryCode&quot;</span><span class="p">)</span>
<span class="n">mapItem</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;mapItem&quot;</span><span class="p">)</span>
<span class="n">isHome</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;isHome&quot;</span><span class="p">)</span>
<span class="n">compoundNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundNames&quot;</span><span class="p">)</span>
<span class="n">compoundSecondaryNames</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;compoundSecondaryNames&quot;</span><span class="p">)</span>
<span class="n">version</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;version&quot;</span><span class="p">)</span>
<span class="n">geoServiceProvider</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;geoServiceProvider&quot;</span><span class="p">)</span>
<span class="n">postalAddress</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;postalAddress&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">(</span>
<span class="n">addressString</span><span class="p">,</span>
<span class="n">countryCode</span><span class="p">,</span>
@@ -353,7 +354,7 @@
<span class="k">class</span> <span class="nc">PLRevGeoMapItem</span><span class="p">:</span>
<span class="sd">"""Stores the list of place names, organized by area"""</span>
<span class="sd">&quot;&quot;&quot;Stores the list of place names, organized by area&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">sortedPlaceInfos</span>
@@ -362,7 +363,7 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="s2">"finalPlaceInfos"</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
@@ -372,23 +373,23 @@
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">]</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">place</span><span class="p">)</span> <span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">]</span>
<span class="k">return</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">"finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">"</span>
<span class="sa">f</span><span class="s2">&quot;finalPlaceInfos: </span><span class="si">{</span><span class="n">finalPlaceInfos</span><span class="si">}</span><span class="s2">, sortedPlaceInfos: </span><span class="si">{</span><span class="n">sortedPlaceInfos</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">sortedPlaceInfos</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"sortedPlaceInfos"</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"finalPlaceInfos"</span><span class="p">)</span>
<span class="n">sortedPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;sortedPlaceInfos&quot;</span><span class="p">)</span>
<span class="n">finalPlaceInfos</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;finalPlaceInfos&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItem</span><span class="p">(</span><span class="n">sortedPlaceInfos</span><span class="p">,</span> <span class="n">finalPlaceInfos</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">:</span>
<span class="sd">"""Additional info about individual places"""</span>
<span class="sd">&quot;&quot;&quot;Additional info about individual places&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">area</span> <span class="o">=</span> <span class="n">area</span>
@@ -399,35 +400,35 @@
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"area"</span><span class="p">,</span> <span class="s2">"name"</span><span class="p">,</span> <span class="s2">"placeType"</span><span class="p">,</span> <span class="s2">"dominantOrderType"</span><span class="p">]</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="s2">&quot;dominantOrderType&quot;</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">"</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;area: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">area</span><span class="si">}</span><span class="s2">, name: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">, placeType: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">placeType</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">area</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">placeType</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">dominantOrderType</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"area"</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"name"</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"placeType"</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"dominantOrderType"</span><span class="p">)</span>
<span class="n">area</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;area&quot;</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">)</span>
<span class="n">placeType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;placeType&quot;</span><span class="p">)</span>
<span class="n">dominantOrderType</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;dominantOrderType&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">(</span>
<span class="n">area</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">placeType</span><span class="p">,</span> <span class="n">dominantOrderType</span>
<span class="p">)</span>
<span class="k">class</span> <span class="nc">CNPostalAddress</span><span class="p">:</span>
<span class="sd">"""postal address for the reverse geolocation info"""</span>
<span class="sd">&quot;&quot;&quot;postal address for the reverse geolocation info&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span>
@@ -453,14 +454,14 @@
<span class="k">return</span> <span class="nb">all</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span> <span class="o">==</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">field</span><span class="p">)</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="p">[</span>
<span class="s2">"_ISOCountryCode"</span><span class="p">,</span>
<span class="s2">"_city"</span><span class="p">,</span>
<span class="s2">"_country"</span><span class="p">,</span>
<span class="s2">"_postalCode"</span><span class="p">,</span>
<span class="s2">"_state"</span><span class="p">,</span>
<span class="s2">"_street"</span><span class="p">,</span>
<span class="s2">"_subAdministrativeArea"</span><span class="p">,</span>
<span class="s2">"_subLocality"</span><span class="p">,</span>
<span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_city&quot;</span><span class="p">,</span>
<span class="s2">&quot;_country&quot;</span><span class="p">,</span>
<span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span>
<span class="s2">&quot;_state&quot;</span><span class="p">,</span>
<span class="s2">&quot;_street&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span>
<span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
@@ -468,7 +469,7 @@
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="k">return</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="nb">map</span><span class="p">(</span>
<span class="nb">str</span><span class="p">,</span>
<span class="p">[</span>
@@ -486,25 +487,25 @@
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">encode_archive</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">archive</span><span class="p">):</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_ISOCountryCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_country</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_city</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_postalCode</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_state</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_street</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subAdministrativeArea</span><span class="p">)</span>
<span class="n">archive</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">_subLocality</span><span class="p">)</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span> <span class="nf">decode_archive</span><span class="p">(</span><span class="n">archive</span><span class="p">):</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_ISOCountryCode"</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_country"</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_city"</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_postalCode"</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_state"</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_street"</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subAdministrativeArea"</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"_subLocality"</span><span class="p">)</span>
<span class="n">_ISOCountryCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_ISOCountryCode&quot;</span><span class="p">)</span>
<span class="n">_country</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_country&quot;</span><span class="p">)</span>
<span class="n">_city</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_city&quot;</span><span class="p">)</span>
<span class="n">_postalCode</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_postalCode&quot;</span><span class="p">)</span>
<span class="n">_state</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_state&quot;</span><span class="p">)</span>
<span class="n">_street</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_street&quot;</span><span class="p">)</span>
<span class="n">_subAdministrativeArea</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subAdministrativeArea&quot;</span><span class="p">)</span>
<span class="n">_subLocality</span> <span class="o">=</span> <span class="n">archive</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;_subLocality&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">CNPostalAddress</span><span class="p">(</span>
<span class="n">_ISOCountryCode</span><span class="p">,</span>
@@ -519,12 +520,12 @@
<span class="c1"># register the classes with bpylist.archiver</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"CNPostalAddress"</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;CNPostalAddress&quot;</span><span class="p">:</span> <span class="n">CNPostalAddress</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">(</span>
<span class="p">{</span><span class="s2">"PLRevGeoMapItemAdditionalPlaceInfo"</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">{</span><span class="s2">&quot;PLRevGeoMapItemAdditionalPlaceInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItemAdditionalPlaceInfo</span><span class="p">}</span>
<span class="p">)</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoMapItem"</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">"PLRevGeoLocationInfo"</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoMapItem&quot;</span><span class="p">:</span> <span class="n">PLRevGeoMapItem</span><span class="p">})</span>
<span class="n">archiver</span><span class="o">.</span><span class="n">update_class_map</span><span class="p">({</span><span class="s2">&quot;PLRevGeoLocationInfo&quot;</span><span class="p">:</span> <span class="n">PLRevGeoLocationInfo</span><span class="p">})</span>
<div class="viewcode-block" id="PlaceInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.PlaceInfo">[docs]</a><span class="k">class</span> <span class="nc">PlaceInfo</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
@@ -560,18 +561,18 @@
<span class="k">class</span> <span class="nc">PlaceInfo4</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &lt;= 4)"""</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &lt;= 4)&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">place_names</span><span class="p">,</span> <span class="n">country_code</span><span class="p">):</span>
<span class="sd">"""place_names: list of place name tuples in ascending order by area</span>
<span class="sd">&quot;&quot;&quot;place_names: list of place name tuples in ascending order by area</span>
<span class="sd"> tuple fields are: modelID, place name, place type, area, e.g.</span>
<span class="sd"> [(5, "St James's Park", 45, 0),</span>
<span class="sd"> (4, 'Westminster', 16, 22097376),</span>
<span class="sd"> (3, 'London', 4, 1596146816),</span>
<span class="sd"> (2, 'England', 2, 180406091776),</span>
<span class="sd"> (1, 'United Kingdom', 1, 414681432064)]</span>
<span class="sd"> [(5, &quot;St James&#39;s Park&quot;, 45, 0),</span>
<span class="sd"> (4, &#39;Westminster&#39;, 16, 22097376),</span>
<span class="sd"> (3, &#39;London&#39;, 4, 1596146816),</span>
<span class="sd"> (2, &#39;England&#39;, 2, 180406091776),</span>
<span class="sd"> (1, &#39;United Kingdom&#39;, 1, 414681432064)]</span>
<span class="sd"> country_code: two letter country code for the country</span>
<span class="sd"> """</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span> <span class="o">=</span> <span class="n">place_names</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_country_code</span> <span class="o">=</span> <span class="n">country_code</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
@@ -610,14 +611,14 @@
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Process place_names to set self._name and self._names"""</span>
<span class="sd">&quot;&quot;&quot;Process place_names to set self._name and self._names&quot;&quot;&quot;</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_place_names</span>
<span class="c1"># build a dictionary where key is placetype</span>
<span class="n">places_dict</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">places</span><span class="p">:</span>
<span class="c1"># places in format:</span>
<span class="c1"># [(5, "St James's Park", 45, 0), ]</span>
<span class="c1"># [(5, &quot;St James&#39;s Park&quot;, 45, 0), ]</span>
<span class="c1"># 0: modelID</span>
<span class="c1"># 1: name</span>
<span class="c1"># 2: type</span>
@@ -659,7 +660,7 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is at most 3 fields and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># always has country if available then either area of interest and city OR</span>
<span class="c1"># city and state</span>
<span class="c1"># e.g. 4, 2, 1 OR 8, 4, 1</span>
@@ -683,61 +684,61 @@
<span class="k">if</span> <span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
<span class="n">name_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="n">name</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">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">name_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__ne__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">return</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__eq__</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</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">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</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">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">PlaceInfo5</span><span class="p">(</span><span class="n">PlaceInfo</span><span class="p">):</span>
<span class="sd">"""Reverse geolocation place info for a photo (Photos &gt;= 5)"""</span>
<span class="sd">&quot;&quot;&quot;Reverse geolocation place info for a photo (Photos &gt;= 5)&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">revgeoloc_bplist</span><span class="p">):</span>
<span class="sd">"""revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object"""</span>
<span class="sd">&quot;&quot;&quot;revgeoloc_bplist: a binary plist blob containing</span>
<span class="sd"> a serialized PLRevGeoLocationInfo object&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_bplist</span> <span class="o">=</span> <span class="n">revgeoloc_bplist</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span> <span class="o">=</span> <span class="n">archiver</span><span class="o">.</span><span class="n">unarchive</span><span class="p">(</span><span class="n">revgeoloc_bplist</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_place_info</span><span class="p">()</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">address_str</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns the postal address as a string"""</span>
<span class="sd">&quot;&quot;&quot;returns the postal address as a string&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">addressString</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">country_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns the country code"""</span>
<span class="sd">&quot;&quot;&quot;returns the country code&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">countryCode</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">ishome</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns True if place is user's home address"""</span>
<span class="sd">&quot;&quot;&quot;returns True if place is user&#39;s home address&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">isHome</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns local place name"""</span>
<span class="sd">&quot;&quot;&quot;returns local place name&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">names</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""returns PlaceNames tuple with detailed reverse geolocation place names"""</span>
<span class="sd">&quot;&quot;&quot;returns PlaceNames tuple with detailed reverse geolocation place names&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_names</span>
<span class="nd">@property</span>
@@ -762,7 +763,7 @@
<span class="k">return</span> <span class="n">postal_address</span>
<span class="k">def</span> <span class="nf">_process_place_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Process sortedPlaceInfos to set self._name and self._names"""</span>
<span class="sd">&quot;&quot;&quot;Process sortedPlaceInfos to set self._name and self._names&quot;&quot;&quot;</span>
<span class="n">places</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_plrevgeoloc</span><span class="o">.</span><span class="n">mapItem</span><span class="o">.</span><span class="n">sortedPlaceInfos</span>
<span class="c1"># build a dictionary where key is placetype</span>
@@ -795,20 +796,20 @@
<span class="c1"># build the name as it appears in Photos</span>
<span class="c1"># the length of the name is variable and appears to be based on available</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by ',')</span>
<span class="c1"># reverse geolocation data in the following order (left to right, joined by &#39;,&#39;)</span>
<span class="c1"># 8: area_of_interest</span>
<span class="c1"># 11: region (I've only seen this applied to islands)</span>
<span class="c1"># 11: region (I&#39;ve only seen this applied to islands)</span>
<span class="c1"># 4: locality / city</span>
<span class="c1"># 2: administrative area (state/province)</span>
<span class="c1"># 1: country</span>
<span class="c1"># 9: inland_water</span>
<span class="c1"># 7: ocean</span>
<span class="n">name</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">name</span> <span class="o">=</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="p">[</span>
<span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="p">[</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">8</span><span class="p">],</span> <span class="c1"># area of interest</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I've only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span> <span class="c1"># region (I&#39;ve only seen this applied to islands)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="c1"># locality / city</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="c1"># administrative area (state/province)</span>
<span class="n">place_names</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="c1"># country</span>
@@ -818,7 +819,7 @@
<span class="k">if</span> <span class="n">p</span> <span class="ow">and</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">]</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">""</span> <span class="k">else</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span> <span class="k">if</span> <span class="n">name</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)):</span>
@@ -831,23 +832,23 @@
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="p">,</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s2">"PlaceInfo("</span> <span class="o">+</span> <span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">='</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">'"</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">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">")"</span>
<span class="k">return</span> <span class="s2">&quot;PlaceInfo(&quot;</span> <span class="o">+</span> <span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#39;</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">&#39;&quot;</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">info</span><span class="o">.</span><span class="n">items</span><span class="p">()])</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">"names"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">"country_code"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">"ishome"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">"address_str"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">"address"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="s2">&quot;names&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">names</span><span class="o">.</span><span class="n">_asdict</span><span class="p">(),</span>
<span class="s2">&quot;country_code&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">country_code</span><span class="p">,</span>
<span class="s2">&quot;ishome&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ishome</span><span class="p">,</span>
<span class="s2">&quot;address_str&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address_str</span><span class="p">,</span>
<span class="s2">&quot;address&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="o">.</span><span class="n">_asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</article>
@@ -886,7 +887,9 @@
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.queryoptions - osxphotos 0.57.1 documentation</title>
<title>osxphotos.queryoptions - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.57.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.57.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.scoreinfo - osxphotos 0.56.4 documentation</title>
<title>osxphotos.scoreinfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.searchinfo - osxphotos 0.56.4 documentation</title>
<title>osxphotos.searchinfo - osxphotos 0.58.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
@@ -123,7 +123,7 @@
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.56.4 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.56.4 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -160,7 +160,7 @@
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
</div>

View File

@@ -58,6 +58,8 @@ Valid filters are:
* `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
* `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
* `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
* `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
* `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
* `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
* `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
@@ -359,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.57.2'
- The osxphotos version, e.g. '0.58.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.57.2',
VERSION: '0.58.2',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -796,6 +796,101 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
<dd><p>Optional argument(s)</p>
</dd></dl>
</section>
<section id="osxphotos-batch-edit">
<h3>batch-edit<a class="headerlink" href="#osxphotos-batch-edit" title="Permalink to this heading">#</a></h3>
<p>Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.</p>
<p>Select one or more photos in Photos then run this command to edit the metadata.</p>
<p>For example:</p>
<div class="line-block">
<div class="line">osxphotos batch-edit </div>
<div class="line">verbose </div>
<div class="line">title “California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}” </div>
<div class="line">description “{place.name}” </div>
<div class="line">keyword “Family” keyword “Travel”</div>
</div>
<p>This will set the title to “California vacation 2023 2023-02-20 001”, and so on,
the description to the reverse geolocation place name,
and add the keywords to “Family”, “Travel”.</p>
<p>title, description, and keyword may be any valid template string.
See <a class="reference external" href="https://rhettbull.github.io/osxphotos/template_help.html">https://rhettbull.github.io/osxphotos/template_help.html</a>
or <cite>osxphotos docs</cite> for more information on the osxphotos template system.</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos batch-edit <span class="o">[</span>OPTIONS<span class="o">]</span>
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-title">
<span class="sig-name descname"><span class="pre">--title</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;TITLE_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-title" title="Permalink to this definition">#</a></dt>
<dd><p>Set title of photo.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-description">
<span class="sig-name descname"><span class="pre">--description</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;DESCRIPTION_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-description" title="Permalink to this definition">#</a></dt>
<dd><p>Set description of photo.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-keyword">
<span class="sig-name descname"><span class="pre">--keyword</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;KEYWORD_TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-keyword" title="Permalink to this definition">#</a></dt>
<dd><p>Add keywords to photo. May be specified multiple times.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-replace-keywords">
<span class="sig-name descname"><span class="pre">--replace-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-replace-keywords" title="Permalink to this definition">#</a></dt>
<dd><p>When specified with keyword, replace existing keywords. Default is to add to existing keywords.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-location">
<span class="sig-name descname"><span class="pre">--location</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;LATITUDE</span> <span class="pre">LONGITUDE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-location" title="Permalink to this definition">#</a></dt>
<dd><p>Set location of photo. Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-dry-run">
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-dry-run" title="Permalink to this definition">#</a></dt>
<dd><p>Dont actually change anything.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-undo">
<span class="sig-name descname"><span class="pre">--undo</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-undo" title="Permalink to this definition">#</a></dt>
<dd><p>Restores photo metadata to what it was prior to the last batch edit. May be combined with dry-run.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-V">
<span id="cmdoption-osxphotos-batch-edit-v"></span><span id="cmdoption-osxphotos-batch-edit-verbose"></span><span class="sig-name descname"><span class="pre">-V</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--verbose</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-V" title="Permalink to this definition">#</a></dt>
<dd><p>Print verbose output; may be specified multiple times for more verbose output.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-timestamp">
<span class="sig-name descname"><span class="pre">--timestamp</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-timestamp" title="Permalink to this definition">#</a></dt>
<dd><p>Add time stamp to verbose output</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-theme">
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;THEME&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-theme" title="Permalink to this definition">#</a></dt>
<dd><p>Specify the color theme to use for output. Valid themes are dark, light, mono, and plain. Defaults to dark or light depending on system dark mode setting.</p>
<dl class="field-list simple">
<dt class="field-odd">Options<span class="colon">:</span></dt>
<dd class="field-odd"><p>dark | light | mono | plain</p>
</dd>
</dl>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-batch-edit-library">
<span id="cmdoption-osxphotos-batch-edit-db"></span><span class="sig-name descname"><span class="pre">--library</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-batch-edit-library" title="Permalink to this definition">#</a></dt>
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either db or directly as PHOTOS_LIBRARY positional argument. If neither db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
</dd></dl>
</section>
<section id="osxphotos-diff">
<h3>diff<a class="headerlink" href="#osxphotos-diff" title="Permalink to this heading">#</a></h3>
@@ -3746,6 +3841,12 @@ Any args are made available to the python file.</p>
<p>osxphotos show “My Album”</p>
<p>osxphotos show “My Folder”</p>
<p>osxphotos show IMG_1234.JPG</p>
<p>show can also be used to show a photo exported with <cite>osxphotos export</cite>:</p>
<p>osxphotos show /path/to/exported/photo.jpg</p>
<p>In this case, the UUID_OR_NAME is the path to the exported photo and osxphotos
will attempt to find the export database to match the photo to the original in
Photos. If your export database is not in the default location in the root of the
export directory, this will not work.</p>
<p>Notes:</p>
<p>This command requires Photos library version 5 or higher.
Currently this command cannot be used to show subfolders in Photos.</p>
@@ -4459,6 +4560,18 @@ See Timewarp Overview below for additional information.</p>
<dd><p>Set timezone for selected photos as offset from UTC. Format is one of ±HH:MM, ±H:MM, or ±HHMM. The actual time of the photo is not adjusted which means, somewhat counterintuitively, that the time in the new timezone will be different. For example, if photo has time of 12:00 and timezone of GMT+01:00 and new timezone is specified as timezone +02:00 (one hour ahead of current GMT+01:00 timezone), the photos new time will be 13:00 GMT+02:00, which is equivalent to the old time of 12:00+01:00. This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. See also match-time.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-date-added">
<span class="sig-name descname"><span class="pre">--date-added</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;DATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-date-added" title="Permalink to this definition">#</a></dt>
<dd><p>Set date/time added for selected photos. This changes the date added or imported date in Photos but does not change the date/time/timezone of the photo itself. This is useful for removing photos from the Recents album, for example if you have imported old scanned photos. Format is YYYY-MM-DD or YYYY-MM-DD HH:MM:SS. If time is not included, midnight is assumed.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-date-added-from-photo">
<span class="sig-name descname"><span class="pre">--date-added-from-photo</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-date-added-from-photo" title="Permalink to this definition">#</a></dt>
<dd><p>Set date/time added for selected photos to the date/time the photo was taken. This changes the date added or imported date in Photos but does not change the date/time/timezone of the photo itself.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-timewarp-i">
<span id="cmdoption-osxphotos-timewarp-inspect"></span><span class="sig-name descname"><span class="pre">-i</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--inspect</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-timewarp-i" title="Permalink to this definition">#</a></dt>
@@ -4745,6 +4858,7 @@ Commands:
<li><a class="reference internal" href="#osxphotos-about">about</a></li>
<li><a class="reference internal" href="#osxphotos-add-locations">add-locations</a></li>
<li><a class="reference internal" href="#osxphotos-albums">albums</a></li>
<li><a class="reference internal" href="#osxphotos-batch-edit">batch-edit</a></li>
<li><a class="reference internal" href="#osxphotos-diff">diff</a></li>
<li><a class="reference internal" href="#osxphotos-docs">docs</a></li>
<li><a class="reference internal" href="#osxphotos-dump">dump</a></li>

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.57.2 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -437,6 +437,20 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-d">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--date-added
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
--date-added-from-photo
<ul>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added-from-photo">osxphotos-timewarp command line option</a>
</li>
</ul></li>
<li>
@@ -453,6 +467,8 @@
<li><a href="cli.html#cmdoption-osxphotos-library">osxphotos command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">osxphotos-albums command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">osxphotos-diff command line option</a>
</li>
@@ -553,6 +569,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-description">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-description">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-description">osxphotos-export command line option</a>
</li>
@@ -600,6 +618,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-dry-run">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-dry-run">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">osxphotos-exiftool command line option</a>
</li>
@@ -1150,6 +1170,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-keyword">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-keyword">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keyword">osxphotos-export command line option</a>
</li>
@@ -1207,6 +1229,8 @@
<li><a href="cli.html#cmdoption-osxphotos-library">osxphotos command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-library">osxphotos-albums command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-library">osxphotos-diff command line option</a>
</li>
@@ -1286,6 +1310,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-location">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-location">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-location">osxphotos-export command line option</a>
</li>
@@ -2054,6 +2080,8 @@
--replace-keywords
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-replace-keywords">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">osxphotos-exiftool command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">osxphotos-export command line option</a>
@@ -2289,6 +2317,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-theme">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-theme">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">osxphotos-exiftool command line option</a>
</li>
@@ -2341,6 +2371,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-timestamp">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-timestamp">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-timestamp">osxphotos-diff command line option</a>
</li>
@@ -2371,6 +2403,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-title">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-title">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-title">osxphotos-export command line option</a>
</li>
@@ -2427,6 +2461,13 @@
<li><a href="cli.html#cmdoption-osxphotos-export-touch-file">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-exportdb-touch-file">osxphotos-exportdb command line option</a>
</li>
</ul></li>
<li>
--undo
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-undo">osxphotos-batch-edit command line option</a>
</li>
</ul></li>
<li>
@@ -2556,6 +2597,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-V">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
</li>
@@ -2858,6 +2901,8 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-V">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">osxphotos-batch-edit command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
</li>
@@ -3865,6 +3910,37 @@
<li><a href="cli.html#cmdoption-osxphotos-albums-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-albums-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
<li>
osxphotos-batch-edit command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-description">--description</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-dry-run">--dry-run</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-keyword">--keyword</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-library">--library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-location">--location</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-replace-keywords">--replace-keywords</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-timestamp">--timestamp</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-title">--title</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-undo">--undo</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">--verbose</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-batch-edit-V">-V</a>
</li>
</ul></li>
<li>
@@ -4450,6 +4526,8 @@
<li><a href="cli.html#cmdoption-osxphotos-import-arg-FILES">FILES</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-info command line option
@@ -4463,8 +4541,6 @@
<li><a href="cli.html#cmdoption-osxphotos-info-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-inspect command line option
@@ -5197,6 +5273,10 @@
<li><a href="cli.html#cmdoption-osxphotos-timewarp-c">--compare-exif</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-d">--date</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added">--date-added</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-date-added-from-photo">--date-added-from-photo</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-D">--date-delta</a>
</li>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos 0.57.2 documentation</title>
<title>osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -238,6 +238,7 @@
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-about">about</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-add-locations">add-locations</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-albums">albums</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-batch-edit">batch-edit</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-diff">diff</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-docs">docs</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-dump">dump</a></li>

Binary file not shown.

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotoss documentation!" href="index.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Reference" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Python Package Overview - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos Python Package Overview - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.57.2 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -145,7 +145,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Python Reference - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos Python Reference - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -459,7 +459,7 @@ If called in context manager, returns True (execution is delayed until exiting c
<dl class="py method">
<dt class="sig sig-object py" id="osxphotos.ExportDB.get_file_record">
<span class="sig-name descname"><span class="pre">get_file_record</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Union</span><span class="p"><span class="pre">[</span></span><span class="pre">Path</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ExportRecord</span></span></span><a class="reference internal" href="_modules/osxphotos/export_db.html#ExportDB.get_file_record"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#osxphotos.ExportDB.get_file_record" title="Permalink to this definition">#</a></dt>
<dd><p>get info for filename and uuid</p>
<dd><p>get info for filename</p>
<p>Returns: an ExportRecord object or None if filename not found</p>
</dd></dl>

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.57.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.58.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@@ -121,7 +121,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -144,7 +144,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Template System - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos Template System - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@@ -240,6 +240,8 @@
<li><p><cite>join(x)</cite>: Join list of values with delimiter x, e.g. join(,): [a, b, c] =&gt; a,b,c; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is join() which joins values together with no delimiter. e.g. join(): [a, b, c] =&gt; abc.</p></li>
<li><p><cite>append(x)</cite>: Append x to list of values, e.g. append(d): [a, b, c] =&gt; [a, b, c, d].</p></li>
<li><p><cite>prepend(x)</cite>: Prepend x to list of values, e.g. prepend(d): [a, b, c] =&gt; [d, a, b, c].</p></li>
<li><p><cite>appends(x)</cite>: Append s[tring] Append x to each value of list of values, e.g. appends(d): [a, b, c] =&gt; [ad, bd, cd].</p></li>
<li><p><cite>prepends(x)</cite>: Prepend s[tring] x to each value of list of values, e.g. prepends(d): [a, b, c] =&gt; [da, db, dc].</p></li>
<li><p><cite>remove(x)</cite>: Remove x from list of values, e.g. remove(b): [a, b, c] =&gt; [a, c].</p></li>
<li><p><cite>slice(start:stop:step)</cite>: Slice list using same semantics as Pythons list slicing, e.g. slice(1:3): [a, b, c, d] =&gt; [b, c]; slice(1:4:2): [a, b, c, d] =&gt; [b, d]; slice(1:): [a, b, c, d] =&gt; [b, c, d]; slice(:-1): [a, b, c, d] =&gt; [a, b, c]; slice(::-1): [a, b, c, d] =&gt; [d, c, b, a]. See also sslice().</p></li>
<li><p><cite>sslice(start:stop:step)</cite>: [s(tring) slice] Slice values in a list using same semantics as Pythons string slicing, e.g. sslice(1:3):abcd =&gt; bc; sslice(1:4:2): abcd =&gt; bd, etc. See also slice().</p></li>
@@ -611,7 +613,7 @@
</td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.57.2</p></td>
<td><p>The osxphotos version, e.g. 0.58.2</p></td>
</tr>
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
<td><p>The full command line used to run osxphotos</p></td>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>OSXPhotos Tutorial - osxphotos 0.57.2 documentation</title>
<title>OSXPhotos Tutorial - osxphotos 0.58.2 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.57.2 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.58.2 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -147,7 +147,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<span class="sidebar-brand-text">osxphotos 0.57.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.58.2 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -58,6 +58,8 @@ Valid filters are:
* `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
* `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
* `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
* `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
* `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
* `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
* `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
* `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
@@ -359,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.57.2'
- The osxphotos version, e.g. '0.58.2'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

327
examples/batch_edit.py Normal file
View File

@@ -0,0 +1,327 @@
"""
Batch edit currently selected photo metadata using osxphotos.
Run this with `osxphotos run batch_edit.py` or `osxphotos run batch_edit.py --help` for more information.
"""
from __future__ import annotations
import functools
import json
import sys
import click
import photoscript
import osxphotos
from osxphotos.cli import echo, echo_error, kvstore, selection_command, verbose
from osxphotos.cli.param_types import TemplateString
from osxphotos.phototemplate import RenderOptions
from osxphotos.sqlitekvstore import SQLiteKVStore
class Latitude(click.ParamType):
name = "Latitude"
def convert(self, value, param, ctx):
try:
latitude = float(value)
if latitude < -90 or latitude > 90:
raise ValueError
return latitude
except Exception:
self.fail(
f"Invalid latitude {value}. Must be a floating point number between -90 and 90."
)
class Longitude(click.ParamType):
name = "Longitude"
def convert(self, value, param, ctx):
try:
longitude = float(value)
if longitude < -180 or longitude > 180:
raise ValueError
return longitude
except Exception:
self.fail(
f"Invalid longitude {value}. Must be a floating point number between -180 and 180."
)
@selection_command(name="batch-edit")
@click.option(
"--title",
metavar="TITLE_TEMPLATE",
type=TemplateString(),
help="Set title of photo.",
)
@click.option(
"--description",
metavar="DESCRIPTION_TEMPLATE",
type=TemplateString(),
help="Set description of photo.",
)
@click.option(
"--keyword",
metavar="KEYWORD_TEMPLATE",
type=TemplateString(),
multiple=True,
help="Set keywords of photo. May be specified multiple times.",
)
@click.option(
"--location",
metavar="LATITUDE LONGITUDE",
type=click.Tuple([Latitude(), Longitude()]),
help="Set location of photo. "
"Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.",
)
@click.option("--dry-run", is_flag=True, help="Don't actually change anything.")
@click.option(
"--undo",
is_flag=True,
help="Restores photo metadata to what it was prior to the last batch edit. "
"May be combined with --dry-run.",
)
def batch_edit(
photos: list[osxphotos.PhotoInfo],
title,
description,
keyword,
location,
dry_run,
undo,
**kwargs,
):
"""
Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.
Select one or more photos in Photos then run this command to edit the metadata.
For example:
\b
osxphotos run batch_edit.py \\
--verbose \\
--title "California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}" \\
--description "{place.name}" \\
--keyword "Family" --keyword "Travel" --keyword "{keyword}"
This will set the title to "California vacation 2023 2023-02-20 001", and so on,
the description to the reverse geolocation place name,
and the keywords to "Family", "Travel", and any existing keywords of the photo.
--title, --description, and --keyword may be any valid template string.
See https://rhettbull.github.io/osxphotos/template_help.html for more information
on the osxphotos template system.
"""
if not title and not description and not keyword and not location and not undo:
echo_error(
"[error] Must specify at least one of: "
" --title, --description, --keyword, --location, --undo. "
"Use --help for more information."
)
sys.exit(1)
if undo and (title or description or keyword or location):
echo_error(
"[error] Cannot specify --undo and any options other than --dry-run. "
"Use --help for more information."
)
sys.exit(1)
if not photos:
echo_error("[error] No photos selected")
sys.exit(1)
# sort photos by date so that {counter} order is correct
photos.sort(key=lambda p: p.date)
undo_store = kvstore("batch_edit")
verbose(f"Undo database stored in [filepath]{undo_store.path}", level=2)
echo(f"Processing [num]{len(photos)}[/] photos...")
for photo in photos:
verbose(
f"Processing [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
if undo:
undo_photo_edits(photo, undo_store, dry_run)
continue
save_photo_undo_info(undo_store, photo)
set_photo_title_from_template(photo, title, dry_run)
set_photo_description_from_template(photo, description, dry_run)
set_photo_keywords_from_template(photo, keyword, dry_run)
set_photo_location(photo, location, dry_run)
# cache photoscript Photo object to avoid re-creating it for each photo
# maxsize=1 as this function is called repeatedly for each photo then
# the next photo is processed
@functools.lru_cache(maxsize=1)
def photoscript_photo(photo: osxphotos.PhotoInfo) -> photoscript.Photo:
"""Return photoscript Photo object for photo"""
return photoscript.Photo(photo.uuid)
def save_photo_undo_info(undo_store: SQLiteKVStore, photo: osxphotos.PhotoInfo):
"""Save undo information to undo store"""
undo_store[photo.uuid] = photo.json()
def undo_photo_edits(
photo: osxphotos.PhotoInfo, undo_store: SQLiteKVStore, dry_run: bool
):
"""Undo edits for photo"""
if not (undo_info := undo_store.get(photo.uuid)):
verbose(
f"[warning] No undo information for photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
return
undo_info = json.loads(undo_info)
ps_photo = photoscript_photo(photo)
exiting_title, exiting_description, exiting_keywords, exiting_location = (
photo.title,
photo.description,
sorted(photo.keywords),
photo.location,
)
previous_title, previous_description, previous_keywords, previous_location = (
undo_info.get("title"),
undo_info.get("description"),
sorted(undo_info.get("keywords")),
(undo_info.get("latitude"), undo_info.get("longitude")),
)
verbose(
f"Undoing edits for [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
for name, existing, previous in (
("title", exiting_title, previous_title),
("description", exiting_description, previous_description),
("keywords", exiting_keywords, previous_keywords),
("location", exiting_location, previous_location),
):
if existing != previous:
verbose(
f" [i]{name}[/]: [change]{existing}[/] -> [no_change]{previous}[/]"
)
if not dry_run:
setattr(ps_photo, name, previous)
else:
verbose(f" [i]{name} (no change)[/]: [no_change]{existing}[/]", level=2)
def set_photo_title_from_template(
photo: osxphotos.PhotoInfo, title_template: str, dry_run: bool
):
"""Set photo title from template"""
if not title_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
title_string, _ = photo.render_template(title_template, render_options)
title_string = [ts for ts in title_string if ts]
if not title_string:
verbose(
f"No title returned from template, nothing to do: [bold]{title_template}"
)
return
if len(title_string) > 1:
echo_error(
f"[error] Title template must return a single string: [bold]{title_string}"
)
sys.exit(1)
verbose(f"Setting [i]title[/i] to [bold]{title_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.title = title_string[0]
def set_photo_description_from_template(
photo: osxphotos.PhotoInfo, description_template: str, dry_run: bool
):
"""Set photo description from template"""
if not description_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
description_string, _ = photo.render_template(description_template, render_options)
description_string = [ds for ds in description_string if ds]
if not description_string:
verbose(
f"No description returned from template, nothing to do: [bold]{description_template}"
)
return
if len(description_string) > 1:
echo_error(
f"[error] Description template must return a single string: [bold]{description_string}"
)
sys.exit(1)
verbose(f"Setting [i]description[/] to [bold]{description_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.description = description_string[0]
def set_photo_keywords_from_template(
photo: osxphotos.PhotoInfo, keyword_template: list[str], dry_run: bool
):
"""Set photo keywords from template"""
if not keyword_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
keywords = set()
for kw in keyword_template:
kw_string, _ = photo.render_template(kw, render_options)
if kw_string:
# filter out empty strings
keywords.update([k for k in kw_string if k])
if not keywords:
verbose(
f"No keywords returned from template, nothing to do: [bold]{keyword_template}"
)
return
verbose(
f"Setting [i]keywords[/] to {', '.join(f'[bold]{kw}[/]' for kw in keywords)}"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.keywords = list(keywords)
def set_photo_location(
photo: osxphotos.PhotoInfo, location: tuple[float, float], dry_run: bool
):
"""Set photo location"""
if not location or location[0] is None or location[1] is None:
return
latitude, longitude = location
verbose(
f"Setting [i]location[/] to [num]{latitude:.6f}[/], [num]{longitude:.6f}[/]"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.location = (latitude, longitude)
if __name__ == "__main__":
batch_edit()

View File

@@ -123,6 +123,7 @@ _TESTED_OS_VERSIONS = [
("12", "6"),
("13", "0"),
("13", "1"),
("13", "2"),
]
# Photos 5 has persons who are empty string if unidentified face

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.57.2"
__version__ = "0.58.2"

View File

@@ -46,6 +46,7 @@ if args.get("--debug", False):
from .about import about
from .add_locations import add_locations
from .albums import albums
from .batch_edit import batch_edit
from .cli import cli_main
from .cli_commands import (
abort,
@@ -95,6 +96,7 @@ __all__ = [
"about",
"add_locations",
"albums",
"batch_edit",
"cli_main",
"debug_dump",
"diff",

311
osxphotos/cli/batch_edit.py Normal file
View File

@@ -0,0 +1,311 @@
"""
batch-edit command for osxphotos CLI
"""
from __future__ import annotations
import functools
import json
import sys
import click
import photoscript
import osxphotos
from osxphotos.phototemplate import RenderOptions
from osxphotos.sqlitekvstore import SQLiteKVStore
from .cli_commands import echo, echo_error, selection_command, verbose
from .kvstore import kvstore
from .param_types import Latitude, Longitude, TemplateString
@selection_command(name="batch-edit")
@click.option(
"--title",
metavar="TITLE_TEMPLATE",
type=TemplateString(),
help="Set title of photo.",
)
@click.option(
"--description",
metavar="DESCRIPTION_TEMPLATE",
type=TemplateString(),
help="Set description of photo.",
)
@click.option(
"--keyword",
metavar="KEYWORD_TEMPLATE",
type=TemplateString(),
multiple=True,
help="Add keywords to photo. May be specified multiple times.",
)
@click.option(
"--replace-keywords",
is_flag=True,
help="When specified with --keyword, replace existing keywords. "
"Default is to add to existing keywords.",
)
@click.option(
"--location",
metavar="LATITUDE LONGITUDE",
type=click.Tuple([Latitude(), Longitude()]),
help="Set location of photo. "
"Must be specified as a pair of numbers with latitude in the range -90 to 90 and longitude in the range -180 to 180.",
)
@click.option("--dry-run", is_flag=True, help="Don't actually change anything.")
@click.option(
"--undo",
is_flag=True,
help="Restores photo metadata to what it was prior to the last batch edit. "
"May be combined with --dry-run.",
)
def batch_edit(
photos: list[osxphotos.PhotoInfo],
title,
description,
keyword,
replace_keywords,
location,
dry_run,
undo,
**kwargs,
):
"""
Batch edit photo metadata such as title, description, keywords, etc.
Operates on currently selected photos.
Select one or more photos in Photos then run this command to edit the metadata.
For example:
\b
osxphotos batch-edit \\
--verbose \\
--title "California vacation 2023 {created.year}-{created.dd}-{created.mm} {counter:03d}" \\
--description "{place.name}" \\
--keyword "Family" --keyword "Travel"
This will set the title to "California vacation 2023 2023-02-20 001", and so on,
the description to the reverse geolocation place name,
and add the keywords to "Family", "Travel".
--title, --description, and --keyword may be any valid template string.
See https://rhettbull.github.io/osxphotos/template_help.html
or `osxphotos docs` for more information on the osxphotos template system.
"""
if not title and not description and not keyword and not location and not undo:
echo_error(
"[error] Must specify at least one of: "
" --title, --description, --keyword, --location, --undo. "
"Use --help for more information."
)
sys.exit(1)
if undo and (title or description or keyword or location):
echo_error(
"[error] Cannot specify --undo and any options other than --dry-run. "
"Use --help for more information."
)
sys.exit(1)
if replace_keywords and not keyword:
echo_error(
"[error] Cannot specify --replace-keywords without --keyword. "
"Use --help for more information."
)
sys.exit(1)
if not photos:
echo_error("[error] No photos selected")
sys.exit(1)
# sort photos by date so that {counter} order is correct
photos.sort(key=lambda p: p.date)
undo_store = kvstore("batch_edit")
verbose(f"Undo database stored in [filepath]{undo_store.path}", level=2)
echo(f"Processing [num]{len(photos)}[/] photos...")
for photo in photos:
verbose(
f"Processing [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
if undo:
undo_photo_edits(photo, undo_store, dry_run)
continue
save_photo_undo_info(undo_store, photo)
set_photo_title_from_template(photo, title, dry_run)
set_photo_description_from_template(photo, description, dry_run)
set_photo_keywords_from_template(photo, keyword, replace_keywords, dry_run)
set_photo_location(photo, location, dry_run)
# cache photoscript Photo object to avoid re-creating it for each photo
# maxsize=1 as this function is called repeatedly for each photo then
# the next photo is processed
@functools.lru_cache(maxsize=1)
def photoscript_photo(photo: osxphotos.PhotoInfo) -> photoscript.Photo:
"""Return photoscript Photo object for photo"""
return photoscript.Photo(photo.uuid)
def save_photo_undo_info(undo_store: SQLiteKVStore, photo: osxphotos.PhotoInfo):
"""Save undo information to undo store"""
undo_store[photo.uuid] = photo.json()
def undo_photo_edits(
photo: osxphotos.PhotoInfo, undo_store: SQLiteKVStore, dry_run: bool
):
"""Undo edits for photo"""
if not (undo_info := undo_store.get(photo.uuid)):
verbose(
f"[warning] No undo information for photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
return
undo_info = json.loads(undo_info)
ps_photo = photoscript_photo(photo)
exiting_title, exiting_description, exiting_keywords, exiting_location = (
photo.title,
photo.description,
sorted(photo.keywords),
photo.location,
)
previous_title, previous_description, previous_keywords, previous_location = (
undo_info.get("title"),
undo_info.get("description"),
sorted(undo_info.get("keywords")),
(undo_info.get("latitude"), undo_info.get("longitude")),
)
verbose(
f"Undoing edits for [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
)
for name, existing, previous in (
("title", exiting_title, previous_title),
("description", exiting_description, previous_description),
("keywords", exiting_keywords, previous_keywords),
("location", exiting_location, previous_location),
):
if existing != previous:
verbose(
f" [i]{name}[/]: [change]{existing}[/] -> [no_change]{previous}[/]"
)
if not dry_run:
setattr(ps_photo, name, previous)
else:
verbose(f" [i]{name} (no change)[/]: [no_change]{existing}[/]", level=2)
def set_photo_title_from_template(
photo: osxphotos.PhotoInfo, title_template: str, dry_run: bool
):
"""Set photo title from template"""
if not title_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
title_string, _ = photo.render_template(title_template, render_options)
title_string = [ts for ts in title_string if ts]
if not title_string:
verbose(
f"No title returned from template, nothing to do: [bold]{title_template}"
)
return
if len(title_string) > 1:
echo_error(
f"[error] Title template must return a single string: [bold]{title_string}"
)
sys.exit(1)
verbose(f"Setting [i]title[/i] to [bold]{title_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.title = title_string[0]
def set_photo_description_from_template(
photo: osxphotos.PhotoInfo, description_template: str, dry_run: bool
):
"""Set photo description from template"""
if not description_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
description_string, _ = photo.render_template(description_template, render_options)
description_string = [ds for ds in description_string if ds]
if not description_string:
verbose(
f"No description returned from template, nothing to do: [bold]{description_template}"
)
return
if len(description_string) > 1:
echo_error(
f"[error] Description template must return a single string: [bold]{description_string}"
)
sys.exit(1)
verbose(f"Setting [i]description[/] to [bold]{description_string[0]}")
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.description = description_string[0]
def set_photo_keywords_from_template(
photo: osxphotos.PhotoInfo,
keyword_template: list[str],
replace_keywords: bool,
dry_run: bool,
):
"""Set photo keywords from template"""
if not keyword_template:
return
# don't render None values
render_options = RenderOptions(none_str="")
keywords = set()
for kw in keyword_template:
kw_string, _ = photo.render_template(kw, render_options)
if kw_string:
# filter out empty strings
keywords.update([k for k in kw_string if k])
if not keywords:
verbose(
f"No keywords returned from template, nothing to do: [bold]{keyword_template}"
)
return
if not replace_keywords:
keywords.update(photo.keywords)
verbose(
f"Setting [i]keywords[/] to {', '.join(f'[bold]{kw}[/]' for kw in keywords)}"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.keywords = list(keywords)
def set_photo_location(
photo: osxphotos.PhotoInfo, location: tuple[float, float], dry_run: bool
):
"""Set photo location"""
if not location or location[0] is None or location[1] is None:
return
latitude, longitude = location
verbose(
f"Setting [i]location[/] to [num]{latitude:.6f}[/], [num]{longitude:.6f}[/]"
)
if not dry_run:
ps_photo = photoscript_photo(photo)
ps_photo.location = (latitude, longitude)

View File

@@ -13,7 +13,8 @@ from osxphotos._version import __version__
from .about import about
from .add_locations import add_locations
from .albums import albums
from .cli_params import DB_OPTION, DEBUG_OPTIONS, JSON_OPTION
from .batch_edit import batch_edit
from .cli_params import DB_OPTION, DEBUG_OPTIONS, JSON_OPTION, VERSION_OPTION
from .common import OSXPHOTOS_HIDDEN
from .debug_dump import debug_dump
from .docs import docs_command
@@ -57,7 +58,7 @@ CTX_SETTINGS = dict(help_option_names=["-h", "--help"])
@click.group(context_settings=CTX_SETTINGS)
@click.version_option(__version__, "--version", "-v")
@VERSION_OPTION
@DB_OPTION
@JSON_OPTION
@DEBUG_OPTIONS
@@ -109,6 +110,7 @@ for command in [
about,
add_locations,
albums,
batch_edit,
debug_dump,
diff,
docs_command,

View File

@@ -7,7 +7,7 @@ from typing import Any, Callable
import click
from .common import OSXPHOTOS_HIDDEN
from .common import OSXPHOTOS_HIDDEN, print_version
from .param_types import *
__all__ = [
@@ -19,8 +19,9 @@ __all__ = [
"JSON_OPTION",
"QUERY_OPTIONS",
"THEME_OPTION",
"VERBOSE_OPTION",
"TIMESTAMP_OPTION",
"VERBOSE_OPTION",
"VERSION_OPTION",
]
@@ -676,3 +677,12 @@ _TIMESTAMP_PARAMETER = click.Option(
["--timestamp"], is_flag=True, help="Add time stamp to verbose output"
)
TIMESTAMP_OPTION = make_click_option_decorator(_TIMESTAMP_PARAMETER)
_VERSION_PARAMETER = click.Option(
["--version", "-v", "_version_flag"],
is_flag=True,
help="Show the version and exit.",
callback=print_version,
)
VERSION_OPTION = make_click_option_decorator(_VERSION_PARAMETER)

View File

@@ -119,7 +119,7 @@ def rich_echo(
# if not outputting to terminal, use a huge width to avoid wrapping
# otherwise tests fail
width = 10_000
console = get_rich_console() or Console(theme or get_rich_theme(), width=width)
console = get_rich_console() or Console(theme=theme or get_rich_theme(), width=width)
if markdown:
message = Markdown(message)
# Markdown always adds a new line so disable unless explicitly specified

View File

@@ -4,6 +4,8 @@ from __future__ import annotations
import os
import pathlib
import platform
import sys
from datetime import datetime
import click
@@ -13,7 +15,7 @@ from xdg import xdg_config_home, xdg_data_home
import osxphotos
from osxphotos._constants import APP_NAME
from osxphotos._version import __version__
from osxphotos.utils import get_latest_version
from osxphotos.utils import get_latest_version, get_macos_version
# used to show/hide hidden commands
OSXPHOTOS_HIDDEN = not bool(os.getenv("OSXPHOTOS_SHOW_HIDDEN", default=False))
@@ -106,3 +108,13 @@ def check_version():
"to suppress this message and prevent osxphotos from checking for latest version.",
err=True,
)
def print_version(ctx, param, value):
"""Print version, this is a callback for the --version option"""
if not value:
return
click.echo(f"osxphotos, version {__version__}")
click.echo(f"Python {sys.version}")
click.echo(f"macOS {'.'.join(get_macos_version())}, {platform.machine()}")
ctx.exit()

View File

@@ -23,6 +23,8 @@ __all__ = [
"DeprecatedPath",
"ExportDBType",
"FunctionCall",
"Latitude",
"Longitude",
"PathOrStdin",
"StrpDateTimePattern",
"TemplateString",
@@ -274,3 +276,35 @@ class StrpDateTimePattern(click.ParamType):
self.fail(f"Invalid strpdatetime format string: {value}. {e}")
else:
return value
class Latitude(click.ParamType):
name = "Latitude"
def convert(self, value, param, ctx):
try:
latitude = float(value)
if latitude < -90 or latitude > 90:
raise ValueError
return latitude
except Exception:
self.fail(
f"Invalid latitude {value}. Must be a floating point number between -90 and 90."
)
class Longitude(click.ParamType):
name = "Longitude"
def convert(self, value, param, ctx):
try:
longitude = float(value)
if longitude < -180 or longitude > 180:
raise ValueError
return longitude
except Exception:
self.fail(
f"Invalid longitude {value}. Must be a floating point number between -180 and 180."
)

View File

@@ -1,10 +1,12 @@
"""osxphotos show command"""
import pathlib
import re
import click
from osxphotos._constants import UUID_PATTERN
from osxphotos.export_db_utils import get_uuid_for_filepath
from osxphotos.photoscript_utils import (
photoscript_object_from_name,
photoscript_object_from_uuid,
@@ -34,6 +36,15 @@ def show(ctx, db, uuid_or_name):
osxphotos show IMG_1234.JPG
show can also be used to show a photo exported with `osxphotos export`:
osxphotos show /path/to/exported/photo.jpg
In this case, the UUID_OR_NAME is the path to the exported photo and osxphotos
will attempt to find the export database to match the photo to the original in
Photos. If your export database is not in the default location in the root of the
export directory, this will not work.
Notes:
This command requires Photos library version 5 or higher.
@@ -65,6 +76,16 @@ def show(ctx, db, uuid_or_name):
f"Found [filename]{obj_type}[/] with name: [filepath]{uuid_or_name}[/]"
)
obj.spotlight()
elif uuid := get_uuid_for_filepath(pathlib.Path(uuid_or_name).resolve()):
if not (obj := photoscript_object_from_uuid(uuid, db)):
raise ValueError(
f"could not find asset with UUID [uuid]{uuid}[/] for file [filepath]{uuid_or_name}[/]"
)
obj_type = obj.__class__.__name__
echo(
f"Found [filename]{obj_type}[/] from export database: [filepath]{uuid_or_name}[/]"
)
obj.spotlight()
else:
raise ValueError(
f"could not find asset with name [filepath]{uuid_or_name}[/]"

View File

@@ -18,6 +18,8 @@ from osxphotos.datetime_utils import datetime_naive_to_local, datetime_to_new_tz
from osxphotos.exif_datetime_updater import ExifDateTimeUpdater
from osxphotos.exiftool import get_exiftool_path
from osxphotos.photodates import (
get_photo_date_added,
set_photo_date_added,
set_photo_date_from_filename,
update_photo_date_time,
update_photo_from_function,
@@ -111,6 +113,14 @@ For this to work, you'll need to install the third-party exiftool (https://exift
*Note on timezones and times*: In Photos, when you change the timezone, Photos assumes the time itself was correct for the previous timezone and adjusts the time accordingly to the new timezone. E.g. if the photo's time is `13:00` and the timezone is `GMT -07:00` and you adjust the timezone one hour east to `GMT -06:00`, Photos will change the time of the photo to `14:00`. osxphotos timewarp follows this behavior. Using `--match-time` allows you to adjust the timezone but keep the same time without adjustment. For example, if your camera clock was correct but lacked timezone information and you took photos in one timezone but imported them to photos in another, Photos will add the timezone of the computer at time of import. You can use osxphotos timewarp to adjust the timezone but keep the time using `--match-time`.
**Update the date the photos were added to Photos**
`osxphotos timewarp --date-added 2021-09-10`
**Update the date the photos were added to Photos to match the date of the photo**
`osxphotos timewarp --date-added-from-photo`
**Compare the date/time/timezone of selected photos with the date/time/timezone in the photos' original EXIF metadata**
`osxphotos timewarp --compare-exif`
@@ -210,6 +220,25 @@ command which can be used to change the time zone of photos after import.
"This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. "
"See also --match-time. ",
)
@click.option(
"--date-added",
metavar="DATE",
type=DateTimeISO8601(),
help="Set date/time added for selected photos. "
"This changes the date added or imported date in Photos but "
"does not change the date/time/timezone of the photo itself. "
"This is useful for removing photos from the Recents album, "
"for example if you have imported old scanned photos. "
"Format is 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. "
"If time is not included, midnight is assumed.",
)
@click.option(
"--date-added-from-photo",
is_flag=True,
help="Set date/time added for selected photos to the date/time the photo was taken. "
"This changes the date added or imported date in Photos but "
"does not change the date/time/timezone of the photo itself. ",
)
@click.option(
"--inspect",
"-i",
@@ -349,6 +378,8 @@ def timewarp(
time,
time_delta,
timezone,
date_added,
date_added_from_photo,
inspect,
compare_exif,
push_exif,
@@ -379,23 +410,25 @@ def timewarp(
# check constraints
if not any(
[
date,
date_delta,
time,
time_delta,
timezone,
inspect,
compare_exif,
parse_date,
push_exif,
pull_exif,
date_added_from_photo,
date_added,
date_delta,
date,
function,
inspect,
parse_date,
pull_exif,
push_exif,
time_delta,
time,
timezone,
]
):
raise click.UsageError(
"At least one of --date, --date-delta, --time, --time-delta, "
"--timezone, --inspect, --compare-exif, --push-exif, --pull-exif, "
"--parse-date, --function "
"--parse-date, --function, --date-added, or --date-added-from-photo "
"must be specified."
)
@@ -441,15 +474,17 @@ def timewarp(
if (
any(
[
date,
date_added_from_photo,
date_added,
date_delta,
time,
time_delta,
timezone,
push_exif,
pull_exif,
date,
function,
parse_date,
pull_exif,
push_exif,
time_delta,
time,
timezone,
]
)
and not force
@@ -488,6 +523,24 @@ def timewarp(
verbose=verbose,
)
set_photo_date_added_ = partial(
set_photo_date_added,
library_path=library,
verbose=verbose,
date_added=date_added,
)
set_photo_date_added_from_photo_ = partial(
set_photo_date_added,
library_path=library,
verbose=verbose,
)
get_photo_date_added_ = partial(
get_photo_date_added,
library_path=library,
)
if function:
update_photo_from_function_ = partial(
update_photo_from_function,
@@ -505,18 +558,21 @@ def timewarp(
"[filename]filename[/filename], [uuid]uuid[/uuid], "
"[time]photo time (local)[/time], "
"[time]photo time[/time], "
"[tz]timezone offset[/tz], [tz]timezone name[/tz]"
"[tz]timezone offset[/tz], [tz]timezone name[/tz], "
"[time]date added (local)[/time]"
)
for photo in photos:
set_crash_data("photo", f"{photo.uuid} {photo.filename}")
tz_seconds, tz_str, tz_name = tzinfo.get_timezone(photo)
photo_date_local = datetime_naive_to_local(photo.date)
photo_date_tz = datetime_to_new_tz(photo_date_local, tz_seconds)
date_added = datetime_naive_to_local(get_photo_date_added_(photo))
echo(
f"[filename]{photo.filename}[/filename], [uuid]{photo.uuid}[/uuid], "
f"[time]{photo_date_local.strftime(DATETIME_FORMAT)}[/time], "
f"[time]{photo_date_tz.strftime(DATETIME_FORMAT)}[/time], "
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz]"
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz], "
f"[time]{date_added.strftime(DATETIME_FORMAT)}[/time]"
)
sys.exit(0)
@@ -609,6 +665,10 @@ def timewarp(
update_photo_time_for_new_timezone_(photo=photo, new_timezone=timezone)
if timezone:
tz_updater.update_photo(photo)
if date_added:
set_photo_date_added_(photo)
if date_added_from_photo:
set_photo_date_added_from_photo_(photo, date_added=photo.date)
if function:
verbose(f"Calling function [bold]{function[1]}")
photo_path = exif_updater.get_photo_path(photo)

Binary file not shown.

View File

@@ -18,6 +18,7 @@ from io import StringIO
from sqlite3 import Error
from tempfile import TemporaryDirectory
from typing import Any, List, Optional, Tuple, Union
import logging
from tenacity import retry, retry_if_not_exception_type, stop_after_attempt
@@ -42,6 +43,16 @@ MAX_RETRY_ATTEMPTS = 3
MAX_EXPORT_RESULTS_DATA_ROWS = 10
logger = logging.getLogger("osxphotos")
def retry_log_error_no_raise(retry_state):
"""Log error for retry but don't raise exception"""
logger.debug(
f"Error {retry_state.outcome} for {retry_state.fn.__name__}({retry_state.args}, {retry_state.kwargs}); retrying...",
)
def pickle_and_zip(data: Any) -> bytes:
"""
Pickle and gzip data.
@@ -103,7 +114,7 @@ class ExportDB:
@retry(stop=stop_after_attempt(MAX_RETRY_ATTEMPTS))
def get_file_record(self, filename: Union[pathlib.Path, str]) -> "ExportRecord":
"""get info for filename and uuid
"""get info for filename
Returns: an ExportRecord object or None if filename not found
"""
@@ -845,7 +856,10 @@ class ExportDBInMemory(ExportDB):
conn_on_disk.commit()
conn_on_disk.close()
@retry(stop=stop_after_attempt(MAX_RETRY_ATTEMPTS))
@retry(
stop=stop_after_attempt(MAX_RETRY_ATTEMPTS),
retry_error_callback=retry_log_error_no_raise, # #999
)
def close(self):
"""close the database connection"""
if self._conn:

View File

@@ -36,6 +36,8 @@ __all__ = [
"export_db_touch_files",
"export_db_update_signatures",
"export_db_vacuum",
"find_export_db_for_filepath",
"get_uuid_for_filepath",
]
@@ -559,3 +561,20 @@ def find_export_db_for_filepath(filepath: Union[str, pathlib.Path]) -> str:
if fname.is_file() and fname.name == OSXPHOTOS_EXPORT_DB:
return str(fname)
return ""
def get_uuid_for_filepath(filepath: Union[str, pathlib.Path]) -> str:
"""Find the UUID for a given filepath, traversing the directory tree to find the export database.
Args:
filepath (Union[str, pathlib.Path]): path to file or directory
Returns:
str: UUID for file or "" if not found
"""
filepath = pathlib.Path(filepath)
if export_db_path := find_export_db_for_filepath(filepath):
export_root = pathlib.Path(export_db_path).parent
exportdb = ExportDB(export_db_path, export_root)
return record.uuid if (record := exportdb.get_file_record(filepath)) else ""
return ""

View File

@@ -4,11 +4,14 @@ from __future__ import annotations
import datetime
import pathlib
import sqlite3
from typing import Callable
import photoscript
from strpdatetime import strpdatetime
from tenacity import retry, stop_after_attempt, wait_exponential
from ._constants import _DB_TABLE_NAMES
from .datetime_utils import (
datetime_has_tz,
datetime_remove_tz,
@@ -16,9 +19,13 @@ from .datetime_utils import (
datetime_utc_to_local,
utc_offset_seconds,
)
from .photosdb.photosdb_utils import get_photos_library_version
from .phototz import PhotoTimeZone, PhotoTimeZoneUpdater
from .timeutils import update_datetime
from .timezones import Timezone
from .utils import get_last_library_path, get_system_library_path
MACOS_TIME_EPOCH = datetime.datetime(2001, 1, 1, 0, 0, 0)
def update_photo_date_time(
@@ -179,3 +186,94 @@ def set_photo_date_from_filename(
tz_updater.update_photo(photo)
return date
def set_photo_date_added(
photo: photoscript.Photo,
date_added: datetime.datetime,
verbose: Callable[..., None],
library_path: str | None = None,
) -> datetime.datetime | None:
"""Modify the ADDEDDATE of a photo"""
if not (library_path := _get_photos_library_path(library_path)):
raise ValueError("Could not determine Photos library path")
verbose(
f"Setting date added for photo [filename]{photo.filename}[/] to [time]{date_added}[/]"
)
_set_date_added(library_path, photo.uuid, date_added)
photo.date = photo.date + datetime.timedelta(seconds=1)
photo.date = photo.date - datetime.timedelta(seconds=1)
@retry(
wait=wait_exponential(multiplier=1, min=0.100, max=5),
stop=stop_after_attempt(10),
)
def _set_date_added(library_path: str, uuid: str, date_added: datetime.datetime):
"""Set the ADDEDDATE of a photo"""
# Use retry decorator to retry if database is locked
photos_version = get_photos_library_version(library_path)
db_path = str(pathlib.Path(library_path) / "database/Photos.sqlite")
asset_table = _DB_TABLE_NAMES[photos_version]["ASSET"]
timestamp = datetime_to_photos_timestamp(date_added)
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute(
f"UPDATE {asset_table} SET ZADDEDDATE=? WHERE ZUUID=?",
(timestamp, uuid),
)
conn.commit()
conn.close()
def _get_photos_library_path(library_path: str | None = None) -> str:
"""Return path to the Photos library or None if not found"""
# get_last_library_path() returns the path to the last Photos library
# opened but sometimes (rarely) fails on some systems
try:
library_path = (
library_path or get_last_library_path() or get_system_library_path()
)
except Exception:
library_path = None
return library_path
def datetime_to_photos_timestamp(dt: datetime.datetime) -> int:
"""Convert datetime to Photos timestamp (seconds since 2001-01-01)"""
return int((dt - MACOS_TIME_EPOCH).total_seconds())
def photos_timestamp_to_datetime(ts: int) -> datetime.datetime:
"""Convert Photos timestamp (seconds since 2001-01-01) to datetime"""
return MACOS_TIME_EPOCH + datetime.timedelta(seconds=ts)
@retry(
wait=wait_exponential(multiplier=1, min=0.100, max=5),
stop=stop_after_attempt(5),
)
def get_photo_date_added(
photo: photoscript.Photo,
library_path: str | None = None,
) -> datetime.datetime | None:
"""Get the ADDEDDATE of a photo"""
if not (library_path := _get_photos_library_path(library_path)):
raise ValueError("Could not determine Photos library path")
photos_version = get_photos_library_version(library_path)
db_path = str(pathlib.Path(library_path) / "database/Photos.sqlite")
asset_table = _DB_TABLE_NAMES[photos_version]["ASSET"]
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute(
f"SELECT ZADDEDDATE FROM {asset_table} WHERE ZUUID=?",
(photo.uuid,),
)
row = c.fetchone()
conn.close()
return photos_timestamp_to_datetime(row[0])

View File

@@ -1042,9 +1042,8 @@ class PhotosDB:
self._dbphotos[uuid]["lastmodifieddate"] = datetime.fromtimestamp(
row[4] + TIME_DELTA
)
except ValueError:
self._dbphotos[uuid]["lastmodifieddate"] = None
except TypeError:
except (ValueError, TypeError):
# sometimes the date is invalid or null
self._dbphotos[uuid]["lastmodifieddate"] = None
self._dbphotos[uuid]["imageTimeZoneOffsetSeconds"] = row[9]
@@ -1056,8 +1055,8 @@ class PhotosDB:
delta = timedelta(seconds=seconds)
tz = timezone(delta)
self._dbphotos[uuid]["imageDate"] = imagedate.astimezone(tz=tz)
except ValueError:
# sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date
except (ValueError, TypeError):
# sometimes imageDate is invalid so use 1 Jan 1970 as image date
imagedate = datetime(1970, 1, 1)
tz = timezone(timedelta(0))
self._dbphotos[uuid]["imageDate"] = imagedate.astimezone(tz=tz)
@@ -2019,8 +2018,8 @@ class PhotosDB:
delta = timedelta(seconds=seconds)
tz = timezone(delta)
info["imageDate"] = imagedate.astimezone(tz=tz)
except ValueError:
# sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date
except (ValueError, TypeError):
# sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date (#1014)
imagedate = datetime(1970, 1, 1)
tz = timezone(timedelta(0))
info["imageDate"] = imagedate.astimezone(tz=tz)
@@ -2602,8 +2601,8 @@ class PhotosDB:
# save raw time stamp valu
moment_info[date_name + "_timestamp"] = moment_info[date_name]
moment_info[date_name] = moment_date.astimezone(tz=tz)
except ValueError:
# sometimes imageDate is invalid so use 1 Jan 1970 in UTC as image date
except (ValueError, TypeError):
# sometimes imageDate is invalid or null so use 1 Jan 1970 in UTC as image date
moment_date = datetime(1970, 1, 1)
tz = timezone(timedelta(0))
moment_info[date_name + "_timestamp"] = date_stamp

View File

@@ -54,6 +54,8 @@ Valid filters are:
- `join(x)`: Join list of values with delimiter x, e.g. join(,): ['a', 'b', 'c'] => 'a,b,c'; the DELIM option functions similar to join(x) but with DELIM, the join happens before being passed to any filters.May optionally be used without an argument, that is 'join()' which joins values together with no delimiter. e.g. join(): ['a', 'b', 'c'] => 'abc'.
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `appends(x)`: Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].
- `prepends(x)`: Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().

View File

@@ -285,6 +285,8 @@ FILTER_VALUES = {
+ "e.g. join(): ['a', 'b', 'c'] => 'abc'.",
"append(x)": "Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].",
"prepend(x)": "Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].",
"appends(x)": "Append s[tring] Append x to each value of list of values, e.g. appends(d): ['a', 'b', 'c'] => ['ad', 'bd', 'cd'].",
"prepends(x)": "Prepend s[tring] x to each value of list of values, e.g. prepends(d): ['a', 'b', 'c'] => ['da', 'db', 'dc'].",
"remove(x)": "Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].",
"slice(start:stop:step)": "Slice list using same semantics as Python's list slicing, "
+ "e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; "
@@ -543,6 +545,8 @@ class PhotoTemplate:
# process field arguments
if ts.template.fieldarg is not None:
field_arg = ts.template.fieldarg.value
else:
field_arg = None
# process delim
if ts.template.delim is not None:
@@ -1007,15 +1011,17 @@ class PhotoTemplate:
raise SyntaxError(f"Unknown filter: {filter_}")
if filter_ in [
"split",
"chop",
"chomp",
"append",
"appends",
"chomp",
"chop",
"filter",
"prepend",
"prepends",
"remove",
"slice",
"split",
"sslice",
"filter",
] and (args is None or not len(args)):
raise SyntaxError(f"{filter_} requires arguments")
@@ -1032,15 +1038,13 @@ class PhotoTemplate:
elif filter_ == "braces":
value = ["{" + v + "}" for v in values]
elif filter_ == "parens":
value = ["(" + v + ")" for v in values]
value = [f"({v})" for v in values]
elif filter_ == "brackets":
value = ["[" + v + "]" for v in values]
value = [f"[{v}]" for v in values]
elif filter_ == "shell_quote":
value = [shlex.quote(v) for v in values]
elif filter_ == "split":
# split on delimiter
delim = args
if delim:
if delim := args:
new_values = []
for v in values:
new_values.extend(v.split(delim))
@@ -1051,15 +1055,15 @@ class PhotoTemplate:
# chop off characters from the end
try:
chop = int(args)
except ValueError:
raise SyntaxError(f"Invalid value for chop: {args}")
except ValueError as e:
raise SyntaxError(f"Invalid value for chop: {args}") from e
value = [v[:-chop] for v in values] if chop else values
elif filter_ == "chomp":
# chop off characters from the beginning
try:
chomp = int(args)
except ValueError:
raise SyntaxError(f"Invalid value for chomp: {args}")
except ValueError as e:
raise SyntaxError(f"Invalid value for chomp: {args}") from e
value = [v[chomp:] for v in values] if chomp else values
elif filter_ == "autosplit":
# try to split keyword strings automatically
@@ -1094,6 +1098,12 @@ class PhotoTemplate:
elif filter_ == "prepend":
# prepend value to list
value = [args] + values
elif filter_ == "appends":
# append value to each item in list
value = [f"{v}{args}" for v in values]
elif filter_ == "prepends":
# prepend value to each item in list
value = [f"{args}{v}" for v in values]
elif filter_ == "remove":
# remove value from list
value = [v for v in values if v != args]

View File

@@ -51,8 +51,13 @@ class PhotoTimeZone:
self.db_path = db_path
self.ASSET_TABLE = _DB_TABLE_NAMES[photos_version]["ASSET"]
@retry(
wait=wait_exponential(multiplier=1, min=0.100, max=5),
stop=stop_after_attempt(10),
)
def get_timezone(self, photo: Photo) -> Tuple[int, str, str]:
"""Return (timezone_seconds, timezone_str, timezone_name) of photo"""
# Use retry decorator to retry if database is locked
uuid = photo.uuid
sql = f""" SELECT
ZADDITIONALASSETATTRIBUTES.ZTIMEZONEOFFSET,
@@ -119,6 +124,7 @@ class PhotoTimeZoneUpdater:
stop=stop_after_attempt(10),
)
def _update_photo(self, photo: Photo):
# Use retry decorator to retry if database is locked
try:
uuid = photo.uuid
sql = f""" SELECT

View File

@@ -31,10 +31,11 @@ __all__ = [
"dd_to_dms_str",
"expand_and_validate_filepath",
"get_last_library_path",
"get_macos_version",
"get_system_library_path",
"hexdigest",
"increment_filename",
"increment_filename_with_count",
"increment_filename",
"lineno",
"list_directory",
"list_photo_libraries",

View File

@@ -26,6 +26,9 @@ A couple of tests require interaction with Photos and configuring a specific tes
--addalbum: test --add-to-album options
--timewarp: test `osxphotos timewarp`
--test-import: test `osxphotos import`
--test-sync: test `osxphotos sync`
--test-add-locations: test `osxphotos add-locations`
--test-batch-edit: test `osxphotos batch-edit`
## Test Photo Libraries
**Important**: The test code uses several test photo libraries created on various version of MacOS. If you need to inspect one of these or modify one for a test, make a copy of the library (for example, copy it to your ~/Pictures folder) then open the copy in Photos. Once done, copy the revised library back to the tests/ folder. If you do not do this, the Photos background process photoanalysisd will forever try to process the library resulting in updates to the database which will cause git to see changes to the file you didn't intend. I'm not aware of any way to disassociate photoanalysisd from the library once you've opened it in Photos.

View File

@@ -4,7 +4,11 @@ import datetime
import pathlib
import time
from tests.parse_timewarp_output import CompareValues, InspectValues
from tests.parse_timewarp_output import (
CompareValues,
InspectValues,
InspectValuesDateAdded,
)
TEST_LIBRARY_TIMEWARP = "tests/TestTimeWarp-10.15.7.photoslibrary"
@@ -393,4 +397,55 @@ CATALINA_PHOTOS_5 = {
"GMT-0400",
),
},
"date_added": {
# 20230120_010203-0400.jpg
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"data": [
(
"2022-01-01",
InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800"
if not is_dst()
else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2022-01-01 00:00:00-0800"
if not is_dst()
else "2022-01-01 00:00:00-0700",
),
),
(
"2022-01-01 01:02:03",
InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800"
if not is_dst()
else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2022-01-01 01:02:03-0800"
if not is_dst()
else "2022-01-01 01:02:03-0700",
),
),
],
},
"date_added_from_photo": {
# 20230120_010203-0400.jpg
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"expected": InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
),
},
}

View File

@@ -7,7 +7,11 @@ import datetime
import pathlib
import time
from tests.parse_timewarp_output import CompareValues, InspectValues
from tests.parse_timewarp_output import (
CompareValues,
InspectValues,
InspectValuesDateAdded,
)
TEST_LIBRARY_TIMEWARP = "tests/TestTimeWarp-13.1.0.photoslibrary"
@@ -396,4 +400,55 @@ VENTURA_PHOTOS_5 = {
"GMT-0400",
),
},
"date_added": {
# 20230120_010203-0400.jpg
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"data": [
(
"2022-01-01",
InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800"
if not is_dst()
else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2022-01-01 00:00:00-0800"
if not is_dst()
else "2022-01-01 00:00:00-0700",
),
),
(
"2022-01-01 01:02:03",
InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800"
if not is_dst()
else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2022-01-01 01:02:03-0800"
if not is_dst()
else "2022-01-01 01:02:03-0700",
),
),
],
},
"date_added_from_photo": {
# 20230120_010203-0400.jpg
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"expected": InspectValuesDateAdded(
"20230120_010203-0400.jpg",
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
"2023-01-20 01:02:03-0400",
"-0400",
"GMT-0400",
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
),
},
}

View File

@@ -26,6 +26,9 @@ TEST_SYNC = False
# run add-locations tests (configured with --test-add-locations)
TEST_ADD_LOCATIONS = False
# run batch-edit tests (configured with --test-batch-edit)
TEST_BATCH_EDIT = False
# don't clean up crash logs (configured with --no-cleanup)
NO_CLEANUP = False
@@ -124,6 +127,7 @@ def pytest_addoption(parser):
default=False,
help="run `osxphotos import` tests",
)
parser.addoption("--test-batch-edit", action="store_true", default=False)
parser.addoption(
"--test-sync",
action="store_true",
@@ -153,12 +157,14 @@ def pytest_configure(config):
config.getoption("--timewarp"),
config.getoption("--test-import"),
config.getoption("--test-sync"),
config.getoption("--test-batch-edit"),
0,
]
)
> 1
):
pytest.exit(
"--addalbum, --timewarp, --test-import, --test-sync are mutually exclusive"
"--addalbum, --timewarp, --test-import, --test-sync, --test-batch-edit are mutually exclusive"
)
config.addinivalue_line(
@@ -177,6 +183,9 @@ def pytest_configure(config):
"markers",
"test_add_locations: mark test as requiring --test-add-locations to run",
)
config.addinivalue_line(
"markers", "test_batch_edit: mark test as requiring --test-batch-edit to run"
)
# this is hacky but I can't figure out how to check config options in other fixtures
if config.getoption("--timewarp"):
@@ -199,40 +208,44 @@ def pytest_configure(config):
global NO_CLEANUP
NO_CLEANUP = True
if config.getoption("--test-batch-edit"):
global TEST_BATCH_EDIT
TEST_BATCH_EDIT = True
def pytest_collection_modifyitems(config, items):
if not (config.getoption("--addalbum") and TEST_LIBRARY is not None):
skip_addalbum = pytest.mark.skip(
reason="need --addalbum option and MacOS Catalina to run"
)
skip_addalbum = pytest.mark.skip(reason="need --addalbum option to run")
for item in items:
if "addalbum" in item.keywords:
item.add_marker(skip_addalbum)
if not (config.getoption("--timewarp") and TEST_LIBRARY_TIMEWARP is not None):
skip_timewarp = pytest.mark.skip(
reason="need --timewarp option and MacOS Catalina to run"
)
skip_timewarp = pytest.mark.skip(reason="need --timewarp option to run")
for item in items:
if "timewarp" in item.keywords:
item.add_marker(skip_timewarp)
if not (config.getoption("--test-import") and TEST_LIBRARY_IMPORT is not None):
skip_test_import = pytest.mark.skip(
reason="need --test-import option and MacOS Catalina or Ventura to run"
)
skip_test_import = pytest.mark.skip(reason="need --test-import option to run")
for item in items:
if "test_import" in item.keywords:
item.add_marker(skip_test_import)
if not (config.getoption("--test-sync") and TEST_LIBRARY_SYNC is not None):
skip_test_sync = pytest.mark.skip(
reason="need --test-sync option and MacOS Catalina to run"
)
skip_test_sync = pytest.mark.skip(reason="need --test-sync option to run")
for item in items:
if "test_sync" in item.keywords:
item.add_marker(skip_test_sync)
if not (config.getoption("--test-batch-edit")):
skip_test_batch_edit = pytest.mark.skip(
reason="need --test-batch-edit option to run"
)
for item in items:
if "test_batch_edit" in item.keywords:
item.add_marker(skip_test_batch_edit)
if not (
config.getoption("--test-add-locations")
and TEST_LIBRARY_ADD_LOCATIONS is not None

View File

@@ -1,12 +1,34 @@
""" Parse --inspect and --compare-exif output for testing"""
from __future__ import annotations
from collections import namedtuple
from typing import List
# filename, uuid, photo time (local), photo time, timezone offset, timezone name
InspectValues = namedtuple(
"InspectValues",
["filename", "uuid", "date_local", "date_tz", "tz_offset", "tz_name"],
[
"filename",
"uuid",
"date_local",
"date_tz",
"tz_offset",
"tz_name",
],
)
InspectValuesDateAdded = namedtuple(
"InspectValues",
[
"filename",
"uuid",
"date_local",
"date_tz",
"tz_offset",
"tz_name",
"date_added",
],
)
CompareValues = namedtuple(
@@ -22,7 +44,9 @@ CompareValues = namedtuple(
)
def parse_inspect_output(output: str) -> List[InspectValues]:
def parse_inspect_output(
output: str, date_added: bool = False
) -> List[InspectValues] | List[InspectValuesDateAdded]:
"""Parse output of --inspect and return list of InspectValues named tuple"""
lines = [line for line in output.split("\n") if line.strip()]
@@ -32,7 +56,12 @@ def parse_inspect_output(output: str) -> List[InspectValues]:
for line in lines:
parts = line.split(",")
parts = [part.strip() for part in parts]
values.append(InspectValues(*parts))
if not date_added:
# remove date added
parts.pop()
values.append(InspectValues(*parts))
else:
values.append(InspectValuesDateAdded(*parts))
return values

View File

@@ -0,0 +1,220 @@
"""Test osxphotos batch-edit command"""
from __future__ import annotations
import os
import time
import photoscript
import pytest
from click.testing import CliRunner
import osxphotos
from osxphotos.cli.batch_edit import batch_edit
# set timezone to avoid issues with comparing dates
os.environ["TZ"] = "US/Pacific"
time.tzset()
TEST_DATA_BATCH_EDIT = {
"uuid": "F12384F6-CD17-4151-ACBA-AE0E3688539E", # Pumkins1.jpg,
"data": [
(
["--title", "Pumpkin Farm {created.year}-{created.mm}-{created.dd}"],
{"title": "Pumpkin Farm 2018-09-28"},
),
(
[
"--description",
"Pumpkin Farm {created.year}",
"--keyword",
"kids",
"--keyword",
"holiday",
],
{
"description": "Pumpkin Farm 2018",
"keywords": sorted(["kids", "holiday"]),
},
),
(
["--location", "34.052235", "-118.243683"],
{"location": (34.052235, -118.243683)},
),
],
}
def say(msg: str) -> None:
"""Say message with text to speech"""
os.system(f"say {msg}")
def ask_user_to_make_selection(
photoslib: photoscript.PhotosLibrary, suspend_capture, msg: str
) -> list[photoscript.Photo]:
"""Ask user to make selection in Photos and press enter when done"""
with suspend_capture:
photoslib.activate()
say(f"Select the photo of the {msg} in Photos and press enter when done")
input("Press enter when done")
return photoslib.selection
@pytest.mark.test_batch_edit
def test_select_photo(photoslib, suspend_capture):
"""Test batch-edit command"""
photos = ask_user_to_make_selection(
photoslib, suspend_capture, "children lifting the pumpkins"
)
assert len(photos) == 1
photo = photos[0]
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
# initialize the photo's metadata
photo.title = None
photo.description = None
photo.keywords = None
photo.location = None
@pytest.mark.test_batch_edit
@pytest.mark.parametrize("args,expected", TEST_DATA_BATCH_EDIT["data"])
def test_batch_edit(args, expected):
"""Test batch-edit command"""
with CliRunner().isolated_filesystem():
result = CliRunner().invoke(
batch_edit,
[*args, "--dry-run"],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
for key, expected_value in expected.items():
got = getattr(photo, key)
if isinstance(got, list):
got = sorted(got)
assert got != expected_value
result = CliRunner().invoke(
batch_edit,
[*args],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
for key, expected_value in expected.items():
got = getattr(photo, key)
if isinstance(got, list):
got = sorted(got)
assert got == expected_value
@pytest.mark.test_batch_edit
def test_batch_edit_undo(photoslib):
"""Test batch-edit command with --undo"""
photo = photoslib.selection[0]
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
photo.title = "Pumpkin Farm"
photo.description = "Pumpkin Farm"
photo.keywords = ["kids"]
photo.location = (41.256566, -95.940257)
with CliRunner().isolated_filesystem():
result = CliRunner().invoke(
batch_edit,
[
"--title",
"Test",
"--description",
"Test",
"--keyword",
"test",
"--replace-keywords",
"--location",
"34.052235",
"-118.243683",
],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
assert photo.title == "Test"
assert photo.description == "Test"
assert photo.keywords == ["test"]
assert photo.location == (34.052235, -118.243683)
result = CliRunner().invoke(
batch_edit,
["--undo", "--dry-run"],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
assert photo.title == "Test"
assert photo.description == "Test"
assert photo.keywords == ["test"]
assert photo.location == (34.052235, -118.243683)
result = CliRunner().invoke(
batch_edit,
["--undo"],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
assert photo.title == "Pumpkin Farm"
assert photo.description == "Pumpkin Farm"
assert photo.keywords == ["kids"]
assert photo.location == (41.256566, -95.940257)
@pytest.mark.test_batch_edit
def test_batch_edit_replace_keywords(photoslib):
"""Test batch-edit command with --replace-keywords"""
photo = photoslib.selection[0]
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
photo.title = "Pumpkin Farm"
photo.description = "Pumpkin Farm"
photo.keywords = ["kids"]
with CliRunner().isolated_filesystem():
# First test that omitting --replace-keywords adds keywords
result = CliRunner().invoke(
batch_edit,
[
"--keyword",
"test",
],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
assert sorted(photo.keywords) == ["kids", "test"]
result = CliRunner().invoke(
batch_edit,
[
"--keyword",
"test2",
"--replace-keywords",
],
)
assert result.exit_code == 0
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
assert photo.keywords == ["test2"]
@pytest.mark.test_batch_edit
def test_batch_edit_replace_keywords_error(photoslib):
"""Test batch-edit command with --replace-keywords when no keywords specified"""
photo = photoslib.selection[0]
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
with CliRunner().isolated_filesystem():
result = CliRunner().invoke(
batch_edit,
[
"--title",
"test",
"--replace-keywords",
],
)
assert result.exit_code != 0

View File

@@ -1,5 +1,6 @@
""" Tests which require user interaction to run for osxphotos timewarp command """
import datetime
import os
import time
@@ -9,7 +10,11 @@ from click.testing import CliRunner
from osxphotos import PhotosDB
from osxphotos.exiftool import ExifTool
from tests.conftest import get_os_version
from tests.parse_timewarp_output import parse_compare_exif, parse_inspect_output
from tests.parse_timewarp_output import (
InspectValuesDateAdded,
parse_compare_exif,
parse_inspect_output,
)
# set timezone to avoid issues with comparing dates
os.environ["TZ"] = "US/Pacific"
@@ -1055,3 +1060,61 @@ def test_parse_date_tz(photoslib, suspend_capture):
assert output_values[0].date_local == expected.date_local
assert output_values[0].date_tz == expected.date_tz
assert output_values[0].tz_offset == expected.tz_offset
@pytest.mark.timewarp
@pytest.mark.parametrize(
"date_added,expected",
TEST_DATA["date_added"]["data"],
)
def test_date_added(
photoslib, suspend_capture, date_added: str, expected: InspectValuesDateAdded
):
"""Test --date-added"""
from osxphotos.cli.timewarp import timewarp
runner = CliRunner()
result = runner.invoke(
timewarp,
[
"--date-added",
date_added,
"--force",
],
terminal_width=TERMINAL_WIDTH,
)
assert result.exit_code == 0
result = runner.invoke(
timewarp,
["--inspect", "--plain", "--force"],
terminal_width=TERMINAL_WIDTH,
)
output_values = parse_inspect_output(result.output, date_added=True)
assert output_values[0].date_added == expected.date_added
@pytest.mark.timewarp
def test_date_added_from_photo(photoslib, suspend_capture):
"""Test --date-added-from-photo"""
from osxphotos.cli.timewarp import timewarp
runner = CliRunner()
result = runner.invoke(
timewarp,
[
"--date-added-from-photo",
"--force",
],
terminal_width=TERMINAL_WIDTH,
)
assert result.exit_code == 0
result = runner.invoke(
timewarp,
["--inspect", "--plain", "--force"],
terminal_width=TERMINAL_WIDTH,
)
output_values = parse_inspect_output(result.output, date_added=True)
expected = TEST_DATA["date_added_from_photo"]["expected"]
assert output_values[0].date_added == expected.date_added

View File

@@ -78,11 +78,15 @@ TEMPLATE_VALUES_MULTI_KEYWORDS = {
"{; +keyword}": ["flowers; wedding"],
"{; +keyword|titlecase}": ["Flowers; Wedding"],
"{; +keyword|titlecase|parens}": ["(Flowers; Wedding)"],
"{keyword|appends(:keyword)}": ["flowers:keyword", "wedding:keyword"],
"{keyword|prepends(keyword:)}": ["keyword:flowers", "keyword:wedding"],
}
UUID_TITLE = "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"
TEMPLATE_VALUES_TITLE = {
"{title}": ["Tulips tied together at a flower shop"],
"{title|appends(:title)}": ["Tulips tied together at a flower shop:title"],
"{title|prepends(title:)}": ["title:Tulips tied together at a flower shop"],
"{title|titlecase}": ["Tulips Tied Together At A Flower Shop"],
"{title|upper}": ["TULIPS TIED TOGETHER AT A FLOWER SHOP"],
"{title|titlecase|lower|upper}": ["TULIPS TIED TOGETHER AT A FLOWER SHOP"],