Compare commits

...

45 Commits

Author SHA1 Message Date
Rhet Turnbull
eee8ff85ac Added example [skip ci] 2023-08-14 21:15:59 -07:00
Rhet Turnbull
d3a6fed119 Added example [skip ci] 2023-08-14 20:58:54 -07:00
Rhet Turnbull
1b8a254bd8 Updated CHANGELOG.md [skip ci] 2023-08-13 09:53:16 -07:00
Rhet Turnbull
f1cee7959d Updated CHANGELOG.md [skip ci] 2023-08-13 09:52:17 -07:00
Rhet Turnbull
a9843ac60e
Release v0 62 1 (#1158)
* Updated ExportResults docstrings so Sphinx picks them up

* Updated API_README so link works in docs

* Removed sweep templates
2023-08-13 09:47:53 -07:00
Rhet Turnbull
51b1abccf8 Updated CHANGELOG.md [skip ci] 2023-08-13 07:59:00 -07:00
Rhet Turnbull
d7cf66b11f Updated docs 2023-08-13 07:57:55 -07:00
Rhet Turnbull
bc0123ccdb Updated CHANGELOG.md [skip ci] 2023-08-13 07:47:51 -07:00
Rhet Turnbull
d6ca010fa5 Updated CHANGELOG.md [skip ci] 2023-08-12 19:46:51 -07:00
Rhet Turnbull
bb1f93a964
Release v0.62.0 (#1156) 2023-08-12 19:33:04 -07:00
Rhet Turnbull
8be71243d5
Refactor ruff (#1155)
* Refactored to make ruff happy

* Refactored tests, fixed bug in Mojave path_raw
2023-08-12 19:24:46 -07:00
Rhet Turnbull
e53d223f2d
Added shared_library to asdict() (#1154) 2023-08-12 11:21:49 -07:00
sweep-ai[bot]
009e17b75c
Configure Sweep (#1152)
* Create sweep.yaml config file

* Create bugfix template

* Create feature template

* Create refactor template

---------

Co-authored-by: sweep-ai[bot] <128439645+sweep-ai[bot]@users.noreply.github.com>
2023-08-12 11:07:56 -07:00
Rhet Turnbull
7cf28fe0d2
Added shared moment, syndicated, shared library to inspect (#1150) 2023-08-12 10:09:11 -07:00
Rhet Turnbull
0e0b4b1b09
Feature shared icloud library 860 (#1149)
* Initial support for iCloud Shared Library, #860

* Initial implementation of ShareInfo, ShareParticipant
2023-08-12 07:06:49 -07:00
Rhet Turnbull
b833cde599
Feature post function return 1136 (#1147)
* Changed return signature for post_function

* Updated post_function.py example

* Added tests for post_function returns ExportResults
2023-08-06 08:47:42 -07:00
Rhet Turnbull
0ff2d50004
Added API_README to docs (#1146) 2023-08-05 08:41:03 -07:00
Rhet Turnbull
2875b45d6e
Feature post command options 1142 (#1145)
* Added --post-command-break/catch

* Added --post-command-break/catch

* Added --post-command-error and tests

* Fixed help text for --post-command-error
2023-08-05 08:11:47 -07:00
Rhet Turnbull
8fb47d9c40 Fixed typo in template help 2023-08-05 07:35:38 -07:00
Rhet Turnbull
d1d6938581 Fixed typo in template help 2023-08-05 07:31:33 -07:00
Rhet Turnbull
3f74eeff44 Fixed typo in template help, #1143 2023-08-05 07:04:37 -07:00
Rhet Turnbull
93e1966607
Feature catch template error (#1141)
* Starting to implement catch_errors for sidecar_template

* Fixed error printing

* Fixed error printing

* Added tests for catch_errors
2023-08-03 06:33:17 -07:00
Rhet Turnbull
e937285a72
Feature keep file 1135 (#1139)
* Added gitignorefile

* Fixed gitignorefile for os.PathLike paths

* --keep now follows .gitignore rules

* Fixed ruff QA error

* Added support for .osxphotos_keep file

* Added reference to .osxphotos_keep

* Added tests for .osxphotos_keep

* Updated help text for --cleanup, --keep
2023-08-02 06:37:29 -07:00
Rhet Turnbull
284c272183 Updated .gitignore 2023-07-29 12:13:52 -07:00
Rhet Turnbull
db6caa5fa4
Feature custom sidecar zero length (#1138)
* Added skip_zero option to --sidecar-template

* Added missing test file
2023-07-29 10:29:16 -07:00
Rhet Turnbull
ed1486fd4b
Refactored --sidecar-template options (#1137) 2023-07-29 08:54:07 -07:00
Rhet Turnbull
8c49916253 Updated CHANGELOG.md [skip ci] 2023-07-25 07:22:37 -07:00
Rhet Turnbull
201bdacddd
Release v0.61.0 (#1132) 2023-07-25 06:55:23 -07:00
Rhet Turnbull
3999e54f6c
Feature custom sidecar 1123 cache (#1131)
* Added caching to Template, fixed typos

* Added additional sidecar tests
2023-07-25 06:44:39 -07:00
Rhet Turnbull
02b6698c80
Feature custom sidecar 1123 (#1129)
* Working on custom sidecar, #1123

* Working on custom sidecar, #1123

* Added custom sidecar example

* Added WRITE_SKIPPED argument for --sidecar-template

* Updated report writer for user sidecars

* Added noqa to long lines in report writer

* Initial tests for --sidecar-template

* Initial tests for --sidecar-template

* Initial tests for --sidecar-template

* Completed tests for --sidecar-template

* Added json example for --sidecar-template

* Added json example for --sidecar-template
2023-07-24 06:38:44 -07:00
Rhet Turnbull
3bae875a63 Added custom sidecar example 2023-07-22 12:39:18 -07:00
Rhet Turnbull
ea6ecc9767 Updated CHANGELOG.md [skip ci] 2023-07-20 06:30:36 -07:00
Rhet Turnbull
2b1ad4ed7f
Release v0.60.10 (#1128) 2023-07-20 06:22:09 -07:00
Rhet Turnbull
4efc0c9f56
Fixed syndicated photos to work on Photos 7, #1116 (#1127) 2023-07-20 06:20:07 -07:00
Rhet Turnbull
25d4015b65
Added scopes to orphans check (#1126) 2023-07-19 06:14:41 -07:00
Rhet Turnbull
1d46c9ce8b
Feature update photoinfo asdict (#1125)
* Added missing properties to PhotoInfo.asdict()

* Moved new properties to shallow=False
2023-07-19 05:30:57 -07:00
Rhet Turnbull
34fda8bcbc
Added more info to debug-dump --dump photos (#1124) 2023-07-19 05:30:25 -07:00
Rhet Turnbull
45468dd1a9 Updated CHANGELOG.md [skip ci] 2023-07-16 19:08:21 -07:00
allcontributors[bot]
903079a819
add neilpa as a contributor for bug (#1121)
* update README.md [skip ci]

* update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2023-07-16 19:02:34 -07:00
Rhet Turnbull
ff70981fbb
Release v0.60.9 (#1120) 2023-07-16 19:01:52 -07:00
Rhet Turnbull
052f2791ac
Bug shared moments 1116 (#1119)
* Partial for #1116, shared moment photos

* Added --shared-moment, --not-shared-moment query args
2023-07-16 18:14:25 -07:00
Rhet Turnbull
4f0e2a101d Updated CHANGELOG.md [skip ci] 2023-07-16 16:31:25 -07:00
Rhet Turnbull
185c6934b8
Release v0.60.8 (#1118) 2023-07-16 16:26:11 -07:00
Rhet Turnbull
c0fa8ef262
alpha support for macOS Sonoma (Photos 9) (#1117) 2023-07-16 15:09:06 -07:00
Rhet Turnbull
e87a42a3ac Updated CHANGELOG.md [skip ci] 2023-07-15 08:19:48 -07:00
322 changed files with 14895 additions and 1727 deletions

View File

@ -184,7 +184,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/42419?v=4",
"profile": "https://neilpa.me",
"contributions": [
"code"
"code",
"bug"
]
},
{

View File

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

4
.gitignore vendored
View File

@ -1,6 +1,6 @@
.metrics
.DS_store
__pycache__
__pycache__/
.coverage
.condaauto
t.out
@ -17,4 +17,4 @@ cli.spec
docsrc/_build/
venv/
.python-version
cov.xml
cov.xml

View File

@ -57,11 +57,12 @@ osxphotos provides several useful helper functions to make it easy to build simp
Here's a simple example showing how to use the `query_command` decorator to implement a simple command line tool. The `query_command` decorator turns your function into a full-fledged [Click](https://palletsprojects.com/p/click/) command line app that can be run via `osxphotos run example.py` or `python example.py` if you have pip installed osxphotos. Your command will include all the query options available in `osxphotos query` as command line options as well as `--verbose` and other convenient options.
<!--[[[cog
cog.out("```python\n")
cog.out("\n```python\n")
with open("examples/cli_example_1.py", "r") as f:
cog.out(f.read())
cog.out("```\n")
]]]-->
```python
"""Sample query command for osxphotos
@ -136,11 +137,12 @@ if __name__ == "__main__":
Here is a more advanced example that shows how to implement a script with a "dry run" and "resume" capability that preserves state between runs. Using the built-in helpers allows you to implement complex behavior in just a few lines of code.
<!--[[[cog
cog.out("```python\n")
cog.out("\n```python\n")
with open("examples/cli_example_2.py", "r") as f:
cog.out(f.read())
cog.out("```\n")
]]]-->
```python
"""Sample query command for osxphotos
@ -308,11 +310,12 @@ if __name__ == "__main__":
In addition to the `query_command` decorator, you can also use the `selection_command` decorator to implement a command that operates on the current selection in Photos.
<!--[[[cog
cog.out("```python\n")
cog.out("\n```python\n")
with open("examples/cli_example_3.py", "r") as f:
cog.out(f.read())
cog.out("```\n")
]]]-->
```python
"""Sample query command for osxphotos
@ -442,7 +445,7 @@ if __name__ == "__main__":
## Package Interface
### PhotosDB
### <a name="photosdb">PhotosDB</a>
#### Read a Photos library database
@ -633,11 +636,11 @@ For example, in my library, Photos says I have 19,386 photos and 474 movies. Ho
Returns a single PhotoInfo instance for photo with UUID matching `uuid` or None if no photo is found matching `uuid`. If you know the UUID of a photo, `get_photo()` is much faster than `photos`. See also [photos()](#photos).
#### <A name="photosdbquery">`query(options: QueryOptions) -> List[PhotoInfo]:`</a>
#### <A name="photosdb_query">`query(options: QueryOptions) -> List[PhotoInfo]:`</a>
Returns a list of [PhotoInfo](#photoinfo) objects matching the query options. This is preferred method of querying the photos database. See [QueryOptions](#queryoptions) for details on the options available.
#### `keywords`
#### <a name="photosdb_keywords">`keywords`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
@ -646,16 +649,16 @@ keywords = photosdb.keywords
Returns a list of the keywords found in the Photos library
#### <a name="photosdbalbuminfo">`album_info`</a>
#### <a name="photosdb_albuminfo">`album_info`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
albums = photosdb.album_info
```
Returns a list of [AlbumInfo](#albuminfo) objects representing albums in the database or empty list if there are no albums. See also [albums](#albums) and [burst_album_info](#burst_album_info).
Returns a list of [AlbumInfo](#albuminfo) objects representing albums in the database or empty list if there are no albums. See also [albums](#photosdb_albums) and [burst_album_info](#burst_album_info).
#### `albums`
#### <a name="photosdb_albums">`albums`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
@ -666,7 +669,7 @@ Returns a list of the album names found in the Photos library. See also [burst_a
**Note**: In Photos 5.0 (MacOS 10.15/Catalina), It is possible to have more than one album with the same name in Photos. Albums with duplicate names are treated as a single album and the photos in each are combined. For example, if you have two albums named "Wedding" and each has 2 photos, osxphotos will treat this as a single album named "Wedding" with 4 photos in it.
See also [album_info](#album_info.)
See also [album_info](#photosdb_album_info.)
#### `albums_shared`
@ -674,30 +677,30 @@ Returns list of shared album names found in photos database (e.g. albums shared
**Note**: *Only valid for Photos 5 / MacOS 10.15*; on Photos <= 4, prints warning and returns empty list.
#### `import_info`
#### <a name = "photosdb_import_info">`import_info`</a>
Returns a list of [ImportInfo](#importinfo) objects representing the import sessions for the database.
#### `project_info`
#### <a name="photosdb_project_info">`project_info`</a>
Returns a list of [ProjectInfo](#projectinfo) objects representing the projects/creations (cards, calendars, etc.) in the database.
#### `moment_info`
#### <a name="photosdb_moment_info">`moment_info`</a>
Returns the [MomentInfo](#momentinfo) object for the photo or `None` if the photo does not have an associated moment.
#### `folder_info`
#### <a name="photosdb_folder_info">`folder_info`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
folders = photosdb.folder_info
```
Returns a list of [FolderInfo](#folderinfo) objects representing top level folders in the database or empty list if there are no folders. See also [folders](#folders).
Returns a list of [FolderInfo](#folderinfo) objects representing top level folders in the database or empty list if there are no folders. See also [folders](#photosdb_folders).
**Note**: Currently folder_info is only implemented for Photos 5 (Catalina); will return empty list and output warning if called on earlier database versions.
#### `folders`
#### <a name="photosdb_folders">`folders`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
@ -708,16 +711,16 @@ Returns a list names of top level folder names in the database.
**Note**: Currently folders is only implemented for Photos 5 (Catalina); will return empty list and output warning if called on earlier database versions.
#### `persons`
#### <a name="photosdb_persons">`persons`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
persons = photosdb.persons
```
Returns a list of the person names (faces) found in the Photos library. **Note**: It is of course possible to have more than one person with the same name, e.g. "Maria Smith", in the database. `persons` assumes these are the same person and will list only one person named "Maria Smith". If you need more information about persons in the database, see [person_info](#dbpersoninfo).
Returns a list of the person names (faces) found in the Photos library. **Note**: It is of course possible to have more than one person with the same name, e.g. "Maria Smith", in the database. `persons` assumes these are the same person and will list only one person named "Maria Smith". If you need more information about persons in the database, see [person_info](#photosdb_personinfo).
#### <a name="dbpersoninfo">`person_info`</a>
#### <a name="photosdb_person_info">`person_info`</a>
```python
# assumes photosdb is a PhotosDB object (see above)
@ -766,17 +769,17 @@ Returns a dictionary of shared albums (e.g. shared via iCloud photo sharing) fou
**Note**: *Photos 5 / MacOS 10.15 only*. On earlier versions of Photos, prints warning and returns empty dictionary.
#### `labels`
#### <a name="photosdb_labels">`labels`</a>
Returns image categorization labels associated with photos in the library as list of str.
**Note**: Only valid on Photos 5; on earlier versions, returns empty list. In Photos 5, Photos runs machine learning image categorization against photos in the library and automatically assigns labels to photos such as "People", "Dog", "Water", etc. A photo may have zero or more labels associated with it. See also [labels_normalized](#labels_normalized).
**Note**: Only valid on Photos 5; on earlier versions, returns empty list. In Photos 5, Photos runs machine learning image categorization against photos in the library and automatically assigns labels to photos such as "People", "Dog", "Water", etc. A photo may have zero or more labels associated with it. See also [labels_normalized](#photosdb_labels_normalized).
#### `labels_normalized`
#### <a name="photosdb_labels_normalized">`labels_normalized`</a>
Returns image categorization labels associated with photos in the library as list of str. Labels are normalized (e.g. converted to lower case). Use of normalized strings makes it easier to search if you don't how Apple capitalizes a label.
**Note**: Only valid on Photos 5; on earlier versions, returns empty list. In Photos 5, Photos runs machine learning image categorization against photos in the library and automatically assigns labels to photos such as "People", "Dog", "Water", etc. A photo may have zero or more labels associated with it. See also [labels](#labels).
**Note**: Only valid on Photos 5; on earlier versions, returns empty list. In Photos 5, Photos runs machine learning image categorization against photos in the library and automatically assigns labels to photos such as "People", "Dog", "Water", etc. A photo may have zero or more labels associated with it. See also [labels](#photosdb_labels).
#### `labels_as_dict`
@ -949,7 +952,7 @@ if __name__ == "__main__":
print(photo.original_filename, photo.date)
```
### PhotoInfo
### <a name="photoinfo">PhotoInfo</a>
PhotosDB.photos() returns a list of PhotoInfo objects. Each PhotoInfo object represents a single photo in the Photos library.
@ -1009,11 +1012,11 @@ Returns a list of [ProjectInfo](#projectinfo) objects representing projects/crea
Returns a list of the names of the persons in the photo
#### <a name="photopersoninfo">`person_info`</a>
#### <a name="photoinfo_personinfo">`person_info`</a>
Returns a list of [PersonInfo](#personinfo) objects representing persons in the photo. Each PersonInfo object is associated with one or more FaceInfo objects.
#### <a name="photofaceinfo">`face_info`</a>
#### <a name="photooinfo_faceinfo">`face_info`</a>
Returns a list of [FaceInfo](#faceinfo) objects representing faces in the photo. Each face is associated with the a PersonInfo object.
@ -1181,12 +1184,20 @@ Returns True if photo is a [cloud asset](#iscloudasset) and is synched to iCloud
#### `syndicated`
Return true if photo was shared via syndication (e.g. via Messages, etc.); these are photos that appear in "Shared with you" album. Photos 8+ only; returns None if not Photos 8+.
Return true if photo was shared via syndication (e.g. via Messages, etc.); these are photos that appear in "Shared with you" album. Photos 7+ only; returns None if not Photos 7+.
#### `saved_to_library`
Return True if syndicated photo has been saved to library; returns False if photo is not syndicated or has not been saved to the library.
Syndicated photos are photos that appear in "Shared with you" album. Photos 8+ only; returns None if not Photos 8+.
Syndicated photos are photos that appear in "Shared with you" album. Photos 7+ only; returns None if not Photos 7+.
#### `shared_moment`
Return True if photo is part of a shared moment, otherwise False. Shared moments are created when multiple photos are shared via iCloud. (e.g. in Messages)
#### `shared_library`
Return True if photo is included in shared iCloud library, otherwise False. Photos 8+ only; returns False if not Photos 8+.
#### `uti`
@ -1318,19 +1329,19 @@ for photo in photosdb.photos():
**Note**: Only valid on Photos 5+; on earlier versions, returns empty list. In Photos 5+, Photos runs machine learning image categorization against photos in the library and automatically assigns labels to photos such as "People", "Dog", "Water", etc. A photo may have zero or more labels associated with it. See also [labels](#labels).
#### <a name="photosearchinfo">`search_info`</a>
#### <a name="photoinfo_searchinfo">`search_info`</a>
Returns [SearchInfo](#searchinfo) object that represents search metadata for the photo.
**Note**: Only valid on Photos 5+; on ealier versions, returns None.
#### <a name="photosearchinfo-normalized">`search_info_normalized`</a>
#### <a name="photoinfo_search_info_normalized">`search_info_normalized`</a>
Returns [SearchInfo](#searchinfo) object that represents normalized search metadata for the photo. This returns a SearchInfo object just as `search_info` but all the properties of the object return normalized text (converted to lowercase).
**Note**: Only valid on Photos 5+; on ealier versions, returns None.
#### `exif_info`
#### <a name="photoinfo_exif_info">`exif_info`</a>
Returns an [ExifInfo](#exifinfo) object with EXIF details from the Photos database. See [ExifInfo](#exifinfo) for additional details.
@ -1338,7 +1349,7 @@ Returns an [ExifInfo](#exifinfo) object with EXIF details from the Photos databa
See also `exiftool`.
#### `exiftool`
#### <a name="photoinfo_exiftool">`exiftool`</a>
Returns an [ExifToolCaching](#exiftoolExifTool) object for the photo which provides an interface to [exiftool](https://exiftool.org/) allowing you to read the actual EXIF data in the image file inside the Photos library. If [exif_info](#exif-info) doesn't give you all the data you need, you can use `exiftool` to read the entire EXIF contents of the image.
@ -1488,7 +1499,7 @@ Some substitutions, notably `album`, `keyword`, and `person` could return multip
See [Template System](#template-system) for additional details.
#### <a name="detected_text_method">`detected_text(confidence_threshold=TEXT_DETECTION_CONFIDENCE_THRESHOLD)`</a>
#### <a name="photoinfo_detected_text">`detected_text(confidence_threshold=TEXT_DETECTION_CONFIDENCE_THRESHOLD)`</a>
Detects text in photo and returns lists of results as (detected text, confidence)
@ -1504,7 +1515,7 @@ See also [Text Detection](#textdetection).
### ExifInfo
[PhotosInfo.exif_info](#exif-info) returns an `ExifInfo` object with some EXIF data about the photo (Photos 5 only). `ExifInfo` contains the following properties:
[PhotosInfo.exif_info](#photoinfo_exif_info) returns an `ExifInfo` object with some EXIF data about the photo (Photos 5 only). `ExifInfo` contains the following properties:
```python
flash_fired: bool
@ -1542,7 +1553,7 @@ nikon_photos = [
### AlbumInfo
PhotosDB.album_info and PhotoInfo.album_info return a list of AlbumInfo objects. Each AlbumInfo object represents a single album in the Photos library.
[PhotosDB.album_info](#photosdb_album_info) and [PhotoInfo.album_info](photoinfo_album_info) return a list of AlbumInfo objects. Each AlbumInfo object represents a single album in the Photos library.
#### `uuid`
@ -1718,11 +1729,11 @@ Returns the universally unique identifier (uuid) of the folder. This is how Pho
Returns the title or name of the folder.
#### `album_info`
#### <a name="folderinfo_album_info">`album_info`</a>
Returns a list of [AlbumInfo](#albuminfo) objects representing each album contained in the folder.
#### `album_info_shared`
#### <a name="folderinfo_album_info_shared">`album_info_shared`</a>
Returns a list of [AlbumInfo](#albuminfo) objects for each shared album in the photos database.
@ -2033,11 +2044,11 @@ Returns a dictionary representation of the PersonInfo instance.
[PhotoInfo.face_info](#photofaceinfo) return a list of FaceInfo objects representing detected faces in a photo. The FaceInfo class has the following properties and methods.
#### `uuid`
#### <a name="faceinfo_uuid">`uuid`</a>
UUID of the face.
#### `name`
#### <a name="faceinfo_name">`name`</a>
Full name of the person represented by the face or None if person hasn't been given a name in Photos. This is a shortcut for `FaceInfo.person_info.name`.
@ -2045,11 +2056,11 @@ Full name of the person represented by the face or None if person hasn't been gi
UUID of the photo this face is associated with.
#### `person_info`
#### <a name="faceinfo_person_info">`person_info`</a>
[PersonInfo](#personinfo) object associated with this face.
#### `photo`
#### <a name="faceinfo_photo">`photo`</a>
[PhotoInfo](#photoinfo) object representing the photo that contains this face.
@ -2126,11 +2137,11 @@ The following additional properties are also available but are not yet fully doc
* `lip_makeup_type`:
* `smile_type`:
#### `asdict()`
#### <a name="faceinfo_asdict">`asdict()`</a>
Returns a dictionary representation of the FaceInfo instance.
#### `json()`
#### <a name="faceinfo_json">`json()`</a>
Returns a JSON representation of the FaceInfo instance.
@ -2238,8 +2249,9 @@ To get the path of every raw photo, whether it's a single raw photo or a raw+JPE
<!--[[[cog
from osxphotos.phototemplate import get_template_help
cog.out(get_template_help())
cog.out("\n"+get_template_help())
]]]-->
<!-- Generated by cog: see phototemplate.cog.md -->
The templating system converts one or template statements, written in osxphotos metadata templating language, to one or more rendered values using information from the photo being processed.
@ -2391,9 +2403,9 @@ e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{P
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
You can define variables for later use in the template string using the format `{var:NAME,VALUE}` where `VALUE` is a template statement. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
Variables can also be referenced as fields in the template string, for example: `{var:year,{created.year}}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:
@ -2404,8 +2416,9 @@ The following template field substitutions are availabe for use the templating s
<!--[[[cog
from osxphotos.phototemplate import get_template_field_table
cog.out(get_template_field_table())
cog.out("\n"+get_template_field_table()+"\n")
]]]-->
| Field | Description |
|--------------|-------------|
|{name}|Current filename of the photo|
@ -2499,7 +2512,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.60.7'|
|{osxphotos_version}|The osxphotos version, e.g. '0.62.1'|
|{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|
@ -2691,7 +2704,7 @@ Attributes:
### <a name="textdetection">Text Detection</a>
The [PhotoInfo.detected_text()](#detected_text_method) and the `{detected_text}` template will perform text detection on the photos in your library. Text detection is a slow process so to avoid unnecessary re-processing of photos, osxphotos will cache the results of the text detection process as an extended attribute on the photo image file. Extended attributes do not modify the actual file. The extended attribute is named `osxphotos.metadata:detected_text` and can be viewed using the built-in [xattr](https://ss64.com/osx/xattr.html) command or my [osxmetadata](https://github.com/RhetTbull/osxmetadata) tool. If you want to remove the cached attribute, you can do so with `xattr` as follows:
The [PhotoInfo.detected_text()](#photoinfo_detected_text) and the `{detected_text}` template will perform text detection on the photos in your library. Text detection is a slow process so to avoid unnecessary re-processing of photos, osxphotos will cache the results of the text detection process as an extended attribute on the photo image file. Extended attributes do not modify the actual file. The extended attribute is named `osxphotos.metadata:detected_text` and can be viewed using the built-in [xattr](https://ss64.com/osx/xattr.html) command or my [osxmetadata](https://github.com/RhetTbull/osxmetadata) tool. If you want to remove the cached attribute, you can do so with `xattr` as follows:
`find ~/Pictures/Photos\ Library.photoslibrary | xargs -I{} xattr -c osxphotos.metadata:detected_text '{}'`

View File

@ -2,6 +2,168 @@
All notable changes to this project will be documented in this file.
## [v0.62.1](https://github.com/RhetTbull/osxphotos/compare/v0.62.0...v0.62.1)
Documentation Fixes
### [v0.62.1] - 2023-08-13
#### Changed
#### Added
#### Removed
#### Fixed
- Minor fixes to the documentation.
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull)
## [v0.62.0](https://github.com/RhetTbull/osxphotos/compare/v0.61.0...v0.62.0)
Initial support for iCloud Shared Libraries.
This release contains a lot of changes some of which may be breaking depending on your workflow. If you use `--sidecar-template`, you should look at the help for this option as the options have changed from boolean flags to named flags.
If you use `--report` with `--append` and you use CSV reports, you should archive the existing report and start a new one as the report now contains additional fields for user files.
If you use `--post-function`, the called function may now return an [ExportResults](https://rhettbull.github.io/osxphotos/reference.html#osxphotos.ExportResults) object with information about the files written, the files skipped, and any errors generated. If returned, these files will be included in `--report` and will be protected from cleanup with `--cleanup`.
Initial support for photos in Shared iCloud Libraries has been implemented. These photos can be queried via the `--shared-library` option. Still working on decoding the participant information (who is sharing the photos).
### [v0.62.0] - 20230812
#### Added
- Support for iCloud shared libraries, PhotoInfo.shared_library, PhotoInfo.share_participants, PhotoInfo.share_info (#860)
- Shared moment, syndicated, shared library support to `osxphotos inspect`(#860)
- `--post-command-error` option to configure error handling of `--post-command` (#1142)
- `.osxphotos_keep` file can now be used to specify keep patterns for `--cleanup` (#1135)
- Option to `--sidecar-template` to skip zero length files
#### Removed
#### Changed
- Changed `--sidecar-template` options to use named options instead of boolean
- Changed signature of --post-function function to enable it to work with --report, --cleanup (#1136)
- Now can catch template errors with `catch_errors` option to `--sidecar-template`
#### Fixed
- Fixed bug with PhotoInfo.path_raw on Photos <= 4.0
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) - Code and testing
- [@neilpa](https://github.com/neilpa) - The idea for custom sidecars and suggestions to improve the feature
- [@kvisle](https://github.com/kvisle) - For user testing with iCloud shared libraries
## [v0.61.0](https://github.com/RhetTbull/osxphotos/compare/v0.60.10...v0.61.0)
Custom sidecars for osxphotos export
### [v0.61.0] - 20230725
#### Added
- `--sidecar-template` option to export to allow user to specify one or more Mako templates for creating custom sidecars. See [example](https://github.com/RhetTbull/osxphotos/blob/main/examples/custom_sidecar.mako for an example)
#### Removed
#### Changed
- Added `user_sidecar` field to all report formats. This means that if you are using a CSV report with `--append`, you should archive your current report and create a new one which will include the correct headers. For JSON reports, the JSON outpput will simply include a new key for new records. For SQLite reports, the `report` table will be altered to add the new column.
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) - Code and testing
- [@neilpa](https://github.com/neilpa) - The idea for custom sidecars
## [v0.60.10](https://github.com/RhetTbull/osxphotos/compare/v0.60.9...v0.60.10)
Support for syndicated photos on Monterey (Photos 7)
### [v0.60.10] - 2023-07-20
#### Added
#### Removed
#### Changed
- Added additional photo details to `osxphotos debug-dump`
#### Fixed
- Syndicated photos now work on Monterey (#1116)
- `osxphotos orphans` now also scans the scopes directory
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) - code
- [@neilpa](https://github.com/neilpa) - for testing and finding the bug with syndicated photos on Monterey
## [v0.60.9](https://github.com/RhetTbull/osxphotos/compare/v0.60.8...v0.60.9)
Fixed missing path for photos that are part of a shared moment (Ventura+)
### [v0.60.9] - 2023-07-16
#### Added
- `PhotoInfo.shared_moment` property (True if photo is part of a shared moment, otherwise False)
- `--shared-moment`, `--not-shared-moment` query options
#### Removed
#### Changed
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) for code
- [@neilpa](https://github.com/neilpa) for identifying the bug with shared moments
## [v0.60.8](https://github.com/RhetTbull/osxphotos/compare/v0.60.7...v0.60.8)
Adds support for working with Photos libraries on macOS Sonoma (14.0 preview)
### [v0.60.8] - 2023-07-16
#### Added
- Supports Photos libraries created by Photos 9.0 (macOS Sonoma)
#### Removed
#### Changed
#### Contributors
- [@RhetTbull](https://github.com/RhetTbull) - code changes and testing
## [v0.60.7](https://github.com/RhetTbull/osxphotos/compare/v0.60.6...v0.60.7)
AAE Export Support
### [v0.60.7] - 2023-07-15
#### Addeded
- `--export-aae` option for `osxphotos export` to export the raw adjustments plist files
- `PhotoInfo.adjustments_path` property for retrieving the path to the AAE file
#### Removed
#### Changed
#### Contributors
- [@dvdkon](https://github.com/dvdkon) - code changes to add support for AAE files.
## [v0.60.6](https://github.com/RhetTbull/osxphotos/compare/v0.60.5...v0.60.6)
Remove --library/--db from import command

188
README.md
View File

@ -39,10 +39,12 @@ On Linux, macOS-specific features of the CLI will not be available (these will n
The export and query CLI commands as well as the Python API will work on Linux which enables you to export photos
from a Photos library on a Linux machine.
Tested on macOS Sierra (10.12.6) through macOS Ventura (13.3). Tested on both x86 and Apple silicon (M1).
Tested on macOS Sierra (10.12.6) through macOS Ventura (13.4). Tested on both x86 and Apple silicon (M1).
macOS Sonoma (14.0) is currently supported but is still in alpha testing.
| macOS Version | macOS name | Photos.app version |
| ----------------- |------------|:-------------------|
| 14.0 | Sonoma | 9.0 ✅ (alpha support) |
| 13.0 - 13.4 | Ventura | 8.0 ✅ |
| 12.0 - 12.6 | Monterey | 7.0 ✅ |
| 10.16, 11.0-11.4 | Big Sur | 6.0 ✅ |
@ -880,6 +882,14 @@ Options:
to the library
--not-saved-to-library Search for syndicated photos that have not
saved to the library
--shared-moment Search for photos that are part of a shared
moment
--not-shared-moment Search for photos that are not part of a
shared moment
--shared-library Search for photos that are part of a shared
library
--not-shared-library Search for photos that are not part of a
shared library
--regex REGEX TEMPLATE Search for photos where TEMPLATE matches
regular expression REGEX. For example, to find
photos in an album that begins with 'Beach': '
@ -1111,6 +1121,61 @@ Options:
of different types but the same name in the
output directory, e.g. 'IMG_1234.JPG' and
'IMG_1234.MOV'.
--sidecar-template MAKO_TEMPLATE_FILE SIDECAR_FILENAME_TEMPLATE OPTIONS
Create a custom sidecar file for each photo
exported with user provided Mako template
(MAKO_TEMPLATE_FILE). MAKO_TEMPLATE_FILE must
be a valid Mako template (see
https://www.makotemplates.org/). The template
will passed the following variables: photo
(PhotoInfo object for the photo being
exported), sidecar_path (pathlib.Path object
for the path to the sidecar being written),
and photo_path (pathlib.Path object for the
path to the exported photo.
SIDECAR_FILENAME_TEMPLATE must be a valid
template string (see Templating System in
help) which will be rendered to generate the
filename of the sidecar file. The `{filepath}`
template variable may be used in the
SIDECAR_FILENAME_TEMPLATE to refer to the
filename of the photo being exported. OPTIONS
is a comma-separated list of strings providing
additional options to the template. Valid
options are: write_skipped, strip_whitespace,
strip_lines, skip_zero, catch_errors, none.
write_skipped will cause the sidecar file to
be written even if the photo is skipped during
export. If write_skipped is not passed as an
option, the sidecar file will not be written
if the photo is skipped during export.
strip_whitespace and strip_lines indicate
whether or not to strip whitespace and blank
lines, respectively, from the resulting
sidecar file. skip_zero causes the sidecar
file to be skipped if the rendered template is
zero-length. catch_errors causes errors in the
template to be caught and logged but not
raised. Without catch_errors, osxphotos will
abort the export if an error occurs in the
template. For example, to create a sidecar
file with extension .xmp using a template file
named 'sidecar.mako' and write a sidecar for
skipped photos and strip blank lines but not
whitespace: `--sidecar-template sidecar.mako
'{filepath}.xmp' write_skipped,strip_lines`.
To do the same but to drop the photo extension
from the sidecar filename: `--sidecar-template
sidecar.mako
'{filepath.parent}/{filepath.stem}.xmp'
write_skipped,strip_lines`. If you are not
passing any options, you must pass 'none' as
the last argument to --sidecar-template:
`--sidecar-template sidecar.mako
'{filepath}.xmp' none`. For an example Mako
file see https://raw.githubusercontent.com/Rhe
tTbull/osxphotos/main/examples/custom_sidecar.
mako
--exiftool Use exiftool to write metadata directly to
exported photos. To use this option, exiftool
must be installed and in the path. exiftool
@ -1291,30 +1356,59 @@ Options:
scripts or other files. Be sure this is what
you intend before using --cleanup. Use --dry-
run with --cleanup first if you're not
certain.
--keep KEEP_PATH When used with --cleanup, prevents file or
directory KEEP_PATH from being deleted when
cleanup is run. Use this if there are files in
the export directory that you don't want to be
deleted when --cleanup is run. KEEP_PATH may
be a file path, e.g.
'/Volumes/Photos/keep.jpg', or a file path and
wild card, e.g. '/Volumes/Photos/*.txt', or a
directory, e.g. '/Volumes/Photos/KeepMe'.
KEEP_PATH may be an absolute path or a
relative path. If it is relative, it must be
relative to the export destination. For
certain. To prevent files not generated by
osxphotos from being deleted, you may specify
one or more rulesin a file named
`.osxphotos_keep` in the export directory.
This file uses the same format as a .gitignore
file and should contain one rule per line;
lines starting with a `#` will be ignored.
Reference https://git-
scm.com/docs/gitignore#_pattern_format for
details. In addition to the standard
.gitignore rules, the rules may also be the
absolute path to a file or directory. For
example if export destination is
`/Volumes/Photos` and you want to keep all
`.txt` files, you can specify `--keep
"/Volumes/Photos/*.txt"` or `--keep "*.txt"`.
If wild card is used, KEEP_PATH must be
enclosed in quotes to prevent the shell from
expanding the wildcard, e.g. `--keep
"/Volumes/Photos/*.txt"`. If KEEP_PATH is a
directory, all files and directories contained
in KEEP_PATH will be kept. --keep may be
repeated to keep additional files/directories.
`.txt` files, in the top level of the export
directory, you can specify `/*.txt"` in the
.osxphotos_keep file. If you want to keep all
`.txt` files in the export directory and all
subdirectories, you can specify `**/*.txt`. If
present, the .osxphotos_keep file will be read
after the export is completed and any rules
found in the file will be added to the list of
rules to keep. See also --keep.
--keep KEEP_RULE When used with --cleanup, prevents file or
directory matching KEEP_RULE from being
deleted when cleanup is run. Use this if there
are files in the export directory that you
don't want to be deleted when --cleanup is
run. KEEP_RULE follows the same format rules a
.gitignore file. Reference https://git-
scm.com/docs/gitignore#_pattern_format for
details. In addition to the standard
.gitignore rules, KEEP_RULE may also be the
absolute path to a file or directory. For
example if export destination is
`/Volumes/Photos` and you want to keep all
`.txt` files, in the top level of the export
directory, you can specify `--keep "/*.txt"`.
If you want to keep all `.txt` files in the
export directory and all subdirectories, you
can specify `--keep "**/*.txt"`. If wild card
is used, KEEP_RULE must be enclosed in quotes
to prevent the shell from expanding the
wildcard. --keep may be repeated to keep
additional files/directories. Rules may also
be included in a file named `.osxphotos_keep`
in the export directory. If present, this file
will be read after the export is completed and
any rules found in the file will be added to
the list of rules to keep. This file uses the
same format as a .gitignore file and should
contain one rule per line; lines starting with
a `#` will be ignored.
--add-exported-to-album ALBUM Add all exported photos to album ALBUM in
Photos. Album ALBUM will be created if it
doesn't exist. All exported photos will be
@ -1358,8 +1452,18 @@ Options:
full path of all exported files to the file
'exported.txt'. You can run more than one
command by repeating the '--post-command'
option with different arguments. See Post
Command below.
option with different arguments. See also
--post-command-error and --post-function.See
Post Command below.
--post-command-error ACTION Specify either `continue` or `break` for
ACTION to control behavior when a post-command
fails. If `continue`, osxphotos will log the
error and continue processing. If `break`,
osxphotos will stop processing any additional
--post-command commands for the current photo
but will continue with the export. Without
--post-command-error, osxphotos will abort the
export if a post-command encounters an error.
--post-function filename.py::function
Run function on exported files. Use this in
format: --post-function filename.py::function
@ -1781,19 +1885,20 @@ e.g. "{created.year}/{openbrace}{title}{closebrace}" would result in
Variables
You can define variables for later use in the template string using the format
{var:NAME,VALUE}. Variables may then be referenced using the format %NAME.
For example: {var:foo,bar} defines the variable %foo to have value bar. This
can be useful if you want to re-use a complex template value in multiple
places within your template string or for allowing the use of characters that
would otherwise be prohibited in a template string. For example, the "pipe"
(|) character is not allowed in a find/replace pair but you can get around
this limitation like so: {var:pipe,{pipe}}{title[-,%pipe]} which replaces the
- character with | (the value of %pipe).
{var:NAME,VALUE} where VALUE is a template statement. Variables may then be
referenced using the format %NAME. For example: {var:foo,bar} defines the
variable %foo to have value bar. This can be useful if you want to re-use a
complex template value in multiple places within your template string or for
allowing the use of characters that would otherwise be prohibited in a
template string. For example, the "pipe" (|) character is not allowed in a
find/replace pair but you can get around this limitation like so:
{var:pipe,{pipe}}{title[-,%pipe]} which replaces the - character with | (the
value of %pipe).
Variables can also be referenced as fields in the template string, for
example: {var:year,created.year}{original_name}-{%year}. In some cases, use of
variables can make your template string more readable. Variables can be used
as template fields, as values for filters, as values for conditional
example: {var:year,{created.year}}{original_name}-{%year}. In some cases, use
of variables can make your template string more readable. Variables can be
used as template fields, as values for filters, as values for conditional
operations, or as default values. When used as a conditional value or default
value, variables should be treated like any other field and enclosed in braces
as conditional and default values are evaluated as template strings. For
@ -2118,7 +2223,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.60.7'
{osxphotos_version} The osxphotos version, e.g. '0.62.1'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@ -2341,6 +2446,7 @@ OSXPhotos adheres to the [XDG](https://specifications.freedesktop.org/basedir-sp
* `$XDG_DATA_HOME` or `$HOME/.local/share`: `osxphotos` directory containing local data files, for example, the help files displayed with `osxphotos docs`.
* Current working dir: `osxphotos_crash.log` file containing the stack trace of the last crash if OSXPhotos encounters a fatal error during execution.
* export directory (when running `osxphotos export` command): `.osxphotos_export.db` [SQLite](https://www.sqlite.org/index.html) database containing information needed to update an export and track metadata changes in exported photos. *Note*: This file may contain sensitive information such as locations and the names of persons in photos so if you are using `osxphotos export` to share with others, you may want to delete this file. You can also specify an alternate location for the export database using the `--exportdb` flag during export. See also `osxphotos help exportdb` for more information about built in utilities for working with the export database.
* While osxphotos does not create the file, if present in the root of the export directory, osxphotos will read the file `.osxphotos_keep` to load a list of file/directory patterns which should be excluded from `--cleanup` during export. This file uses the same rule format as [.gitignore](https://git-scm.com/docs/gitignore). See `osxphotos help export cleanup` for more information.
## Python API
@ -2500,9 +2606,9 @@ e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{P
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
You can define variables for later use in the template string using the format `{var:NAME,VALUE}` where `VALUE` is a template statement. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
Variables can also be referenced as fields in the template string, for example: `{var:year,{created.year}}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:
@ -2605,7 +2711,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.60.7'|
|{osxphotos_version}|The osxphotos version, e.g. '0.62.1'|
|{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|
@ -2679,7 +2785,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/narensankar0529"><img src="https://avatars3.githubusercontent.com/u/74054766?v=4?s=75" width="75px;" alt="narensankar0529"/><br /><sub><b>narensankar0529</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anarensankar0529" title="Bug reports">🐛</a> <a href="#userTesting-narensankar0529" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/martinhrpi"><img src="https://avatars2.githubusercontent.com/u/19407684?v=4?s=75" width="75px;" alt="Martin"/><br /><sub><b>Martin</b></sub></a><br /><a href="#research-martinhrpi" title="Research">🔬</a> <a href="#userTesting-martinhrpi" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/davidjroos"><img src="https://avatars.githubusercontent.com/u/15630844?v=4?s=75" width="75px;" alt="davidjroos "/><br /><sub><b>davidjroos </b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=davidjroos" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://neilpa.me"><img src="https://avatars.githubusercontent.com/u/42419?v=4?s=75" width="75px;" alt="Neil Pankey"/><br /><sub><b>Neil Pankey</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=neilpa" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://neilpa.me"><img src="https://avatars.githubusercontent.com/u/42419?v=4?s=75" width="75px;" alt="Neil Pankey"/><br /><sub><b>Neil Pankey</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=neilpa" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aneilpa" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://aaronweb.net/"><img src="https://avatars.githubusercontent.com/u/604665?v=4?s=75" width="75px;" alt="Aaron van Geffen"/><br /><sub><b>Aaron van Geffen</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=AaronVanGeffen" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ubrandes"><img src="https://avatars.githubusercontent.com/u/59647284?v=4?s=75" width="75px;" alt="ubrandes "/><br /><sub><b>ubrandes </b></sub></a><br /><a href="#ideas-ubrandes" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>

View File

@ -30,6 +30,12 @@ python3 utils/generate_template_docs.py
m2r2 docsrc/source/template_help.md
rm docsrc/source/template_help.md
echo "Copying API_README.md to docsrc/source/API_README.md"
rm docsrc/source/API_README.rst
cp API_README.md docsrc/source/API_README.md
m2r2 docsrc/source/API_README.md
rm docsrc/source/API_README.md
# build docs
echo "Building docs"
(cd docsrc && make github && make pdf)

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: b4797f5f0cba9ef01218a6f2b8eb0959
config: 2fc476010affcee784f205bf294f4e73
tags: 645f666f9bcd5a90fca523b33c5a78b7

File diff suppressed because it is too large Load Diff

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.60.7 documentation</title>
<title>Overview: module code - osxphotos 0.62.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.60.7 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.62.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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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,6 +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="../API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../reference.html">OSXPhotos Python Reference</a></li>
</ul>

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.60.5 documentation</title>
<title>osxphotos._constants - osxphotos 0.62.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -220,15 +221,18 @@
<span class="c1"># Photos 3.0 (10.13.6) == 3301</span>
<span class="c1"># Photos 4.0 (10.14.5) == 4016</span>
<span class="c1"># Photos 4.0 (10.14.6) == 4025</span>
<span class="c1"># Photos 5.0 (10.15.0) == 6000 or 5001</span>
<span class="c1"># Photos 5.0+ (10.15.0) == 6000 or 5001</span>
<span class="n">_TESTED_DB_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;6000&quot;</span><span class="p">,</span> <span class="s2">&quot;5001&quot;</span><span class="p">,</span> <span class="s2">&quot;4025&quot;</span><span class="p">,</span> <span class="s2">&quot;4016&quot;</span><span class="p">,</span> <span class="s2">&quot;3301&quot;</span><span class="p">,</span> <span class="s2">&quot;2622&quot;</span><span class="p">]</span>
<span class="c1"># database model versions (applies to Photos 5, Photos 6)</span>
<span class="c1"># database model versions (applies to Photos 5+)</span>
<span class="c1"># these come from PLModelVersion key in binary plist in Z_METADATA.Z_PLIST</span>
<span class="c1"># Photos 5 (10.15.1) == 13537</span>
<span class="c1"># Photos 5 (10.15.4, 10.15.5, 10.15.6) == 13703</span>
<span class="c1"># Photos 6 (10.16.0 Beta) == 14104</span>
<span class="n">_TEST_MODEL_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;13537&quot;</span><span class="p">,</span> <span class="s2">&quot;13703&quot;</span><span class="p">,</span> <span class="s2">&quot;14104&quot;</span><span class="p">]</span>
<span class="c1"># Photos 7 (12.0.1) == 15323</span>
<span class="c1"># Photos 8 (13.0.0) == 16320</span>
<span class="c1"># Photos 9 (14.0.0 dev preview) = 17120</span>
<span class="n">_TEST_MODEL_VERSIONS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;13537&quot;</span><span class="p">,</span> <span class="s2">&quot;13703&quot;</span><span class="p">,</span> <span class="s2">&quot;14104&quot;</span><span class="p">,</span> <span class="s2">&quot;15323&quot;</span><span class="p">,</span> <span class="s2">&quot;16320&quot;</span><span class="p">,</span> <span class="s2">&quot;17120&quot;</span><span class="p">]</span>
<span class="n">_PHOTOS_2_VERSION</span> <span class="o">=</span> <span class="s2">&quot;2622&quot;</span>
@ -236,7 +240,7 @@
<span class="n">_PHOTOS_3_VERSION</span> <span class="o">=</span> <span class="s2">&quot;3301&quot;</span>
<span class="c1"># versions 5.0 and later have a different database structure</span>
<span class="n">_PHOTOS_4_VERSION</span> <span class="o">=</span> <span class="s2">&quot;4025&quot;</span> <span class="c1"># latest Mojove version on 10.14.6</span>
<span class="n">_PHOTOS_4_VERSION</span> <span class="o">=</span> <span class="s2">&quot;4025&quot;</span> <span class="c1"># latest Mojave version on 10.14.6</span>
<span class="n">_PHOTOS_5_VERSION</span> <span class="o">=</span> <span class="s2">&quot;5000&quot;</span> <span class="c1"># I&#39;ve seen both 5001 and 6000. 6000 is most common on Catalina and up but there are some version 5001 database in the wild</span>
<span class="c1"># Ranges for model version by Photos version</span>
@ -244,8 +248,15 @@
<span class="n">_PHOTOS_6_MODEL_VERSION</span> <span class="o">=</span> <span class="p">[</span><span class="mi">14000</span><span class="p">,</span> <span class="mi">14999</span><span class="p">]</span>
<span class="n">_PHOTOS_7_MODEL_VERSION</span> <span class="o">=</span> <span class="p">[</span><span class="mi">15000</span><span class="p">,</span> <span class="mi">15999</span><span class="p">]</span> <span class="c1"># Dev preview: 15134, 12.1: 15331</span>
<span class="n">_PHOTOS_8_MODEL_VERSION</span> <span class="o">=</span> <span class="p">[</span><span class="mi">16000</span><span class="p">,</span> <span class="mi">16999</span><span class="p">]</span> <span class="c1"># Ventura dev preview: 16119</span>
<span class="n">_PHOTOS_9_MODEL_VERSION</span> <span class="o">=</span> <span class="p">[</span><span class="mi">17000</span><span class="p">,</span> <span class="mi">17999</span><span class="p">]</span> <span class="c1"># Sonoma dev preview: 17120</span>
<span class="c1"># some table names differ between Photos 5 and Photos 6</span>
<span class="c1"># the preview versions of 12.0.0 had a difference schema for syndication info so need to check model version before processing</span>
<span class="n">_PHOTOS_SYNDICATION_MODEL_VERSION</span> <span class="o">=</span> <span class="mi">15323</span> <span class="c1"># 12.0.1</span>
<span class="c1"># shared iCloud library versions; dev preview doesn&#39;t contain same columns as release version</span>
<span class="n">_PHOTOS_SHARED_LIBRARY_VERSION</span> <span class="o">=</span> <span class="mi">16320</span> <span class="c1"># 13.0</span>
<span class="c1"># some table names differ between Photos 5 and later versions</span>
<span class="n">_DB_TABLE_NAMES</span> <span class="o">=</span> <span class="p">{</span>
<span class="mi">5</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZGENERICASSET&quot;</span><span class="p">,</span>
@ -258,6 +269,8 @@
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_26ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZCUSTOMRENDEREDVALUE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZPERSON&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZASSET&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">6</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
@ -270,6 +283,8 @@
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS.Z_26ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_26ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZCUSTOMRENDEREDVALUE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZPERSON&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZASSET&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">7</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
@ -282,6 +297,8 @@
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS.Z_27ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_27ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZHDRTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZPERSON&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZASSET&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">8</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
@ -294,6 +311,22 @@
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_28ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZHDRTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZPERSON&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZASSET&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="mi">9</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;ASSET&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET&quot;</span><span class="p">,</span>
<span class="s2">&quot;KEYWORD_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_1KEYWORDS.Z_40KEYWORDS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ALBUM_SORT_ORDER&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_FOK_3ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;IMPORT_FOK&quot;</span><span class="p">:</span> <span class="s2">&quot;null&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEPTH_STATE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZASSET.ZDEPTHTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;UTI_ORIGINAL&quot;</span><span class="p">:</span> <span class="s2">&quot;ZINTERNALRESOURCE.ZCOMPACTUTI&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_JOIN&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS.Z_28ALBUMS&quot;</span><span class="p">,</span>
<span class="s2">&quot;ASSET_ALBUM_TABLE&quot;</span><span class="p">:</span> <span class="s2">&quot;Z_28ASSETS&quot;</span><span class="p">,</span>
<span class="s2">&quot;HDR_TYPE&quot;</span><span class="p">:</span> <span class="s2">&quot;ZHDRTYPE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZPERSONFORFACE&quot;</span><span class="p">,</span>
<span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">:</span> <span class="s2">&quot;ZDETECTEDFACE.ZASSETFORFACE&quot;</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
@ -324,6 +357,7 @@
<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="s2">&quot;13&quot;</span><span class="p">,</span> <span class="s2">&quot;3&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;4&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;14&quot;</span><span class="p">,</span> <span class="s2">&quot;0&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.59.2 documentation</title>
<title>osxphotos.albuminfo - osxphotos 0.61.0 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.59.2 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.59.2 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.debug - osxphotos 0.58.1 documentation</title>
<title>osxphotos.debug - osxphotos 0.61.0 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.58.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.58.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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,36 +1,200 @@
<!doctype html>
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<!DOCTYPE html>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.exifinfo - osxphotos 0.61.0 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" />
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.exifinfo &#8212; osxphotos 0.47.9 documentation</title>
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css" />
<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/doctools.js"></script>
<link rel="index" title="Index" href="../../genindex.html" />
<link rel="search" title="Search" href="../../search.html" />
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<span class="sidebar-brand-text">osxphotos 0.61.0 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
</div>
</div>
<div class="body" role="main">
<h1>Source code for osxphotos.exifinfo</h1><div class="highlight"><pre>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<h1>Source code for osxphotos.exifinfo</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot; ExifInfo class to expose EXIF info from the library &quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
@ -62,72 +226,47 @@
<span class="n">codec</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">lens_model</span><span class="p">:</span> <span class="nb">str</span></div>
</pre></div>
</div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../index.html">Documentation overview</a><ul>
<li><a href="../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</footer>
</div>
<div class="footer">
&copy;2021, Rhet Turnbull.
<aside class="toc-drawer no-toc">
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>
</body>
</aside>
</div>
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="../../_static/doctools.js"></script>
<script src="../../_static/sphinx_highlight.js"></script>
<script src="../../_static/scripts/furo.js"></script>
<script src="../../_static/clipboard.min.js"></script>
<script src="../../_static/copybutton.js"></script>
</body>
</html>

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.59.1 documentation</title>
<title>osxphotos.exiftool - osxphotos 0.62.0 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.59.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.0 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.59.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -255,7 +256,7 @@
<span class="k">def</span> <span class="nf">escape_str</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;escape string for use with exiftool -E&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</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">s</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">return</span> <span class="n">s</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="s2">&quot;&amp;#xa;&quot;</span><span class="p">)</span>
@ -266,7 +267,7 @@
<span class="k">def</span> <span class="nf">unescape_str</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;unescape an HTML string returned by exiftool -E&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</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">s</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">return</span> <span class="n">s</span>
<span class="c1"># avoid &quot; in values which result in json.loads() throwing an exception, #636</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;&amp;quot;&quot;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\\</span><span class="s1">&quot;&#39;</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.export_db - osxphotos 0.60.5 documentation</title>
<title>osxphotos.export_db - osxphotos 0.61.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.fileutil - osxphotos 0.60.5 documentation</title>
<title>osxphotos.fileutil - osxphotos 0.61.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.momentinfo - osxphotos 0.48.3 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
<title>osxphotos.momentinfo - osxphotos 0.61.0 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.48.3 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.48.3 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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,91 +195,91 @@
</div>
<article role="main">
<h1>Source code for osxphotos.momentinfo</h1><div class="highlight"><pre>
<span></span><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"MomentInfo"</span><span class="p">]</span>
<span class="sd">"""MomentInfo class with details about photo moments."""</span>
<span></span><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;MomentInfo&quot;</span><span class="p">]</span>
<span class="sd">&quot;&quot;&quot;MomentInfo class with details about photo moments.&quot;&quot;&quot;</span>
<div class="viewcode-block" id="MomentInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.MomentInfo">[docs]</a><span class="k">class</span> <span class="nc">MomentInfo</span><span class="p">:</span>
<span class="sd">"""Info about a photo moment"""</span>
<span class="sd">&quot;&quot;&quot;Info about a photo moment&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="p">,</span> <span class="n">moment_pk</span><span class="p">):</span>
<span class="sd">"""Initialize with a moment PK; returns None if PK not found."""</span>
<span class="sd">&quot;&quot;&quot;Initialize with a moment PK; returns None if PK not found.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span> <span class="o">=</span> <span class="n">db</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pk</span> <span class="o">=</span> <span class="n">moment_pk</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_moment</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_moment_pk</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">moment_pk</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"No moment with PK </span><span class="si">{</span><span class="n">moment_pk</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;No moment with PK </span><span class="si">{</span><span class="n">moment_pk</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">pk</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Primary key of the moment."""</span>
<span class="sd">&quot;&quot;&quot;Primary key of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pk</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">location</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Location of the moment."""</span>
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"latitude"</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"longitude"</span><span class="p">))</span>
<span class="sd">&quot;&quot;&quot;Location of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;latitude&quot;</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;longitude&quot;</span><span class="p">))</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Title of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"title"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Title of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;title&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">subtitle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Subtitle of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"subtitle"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Subtitle of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;subtitle&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">start_date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Start date of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"startDate"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Start date of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;startDate&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">end_date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Stop date of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"endDate"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Stop date of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;endDate&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Date of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"representativeDate"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Date of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;representativeDate&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">modification_date</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Modification date of the moment."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"modificationDate"</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;Modification date of the moment.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_moment</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;modificationDate&quot;</span><span class="p">)</span>
<span class="nd">@property</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="sd">"""All photos in this moment"""</span>
<span class="sd">&quot;&quot;&quot;All photos in this moment&quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photos</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">photo_uuids</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">uuid</span>
<span class="k">for</span> <span class="n">uuid</span><span class="p">,</span> <span class="n">photo</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_dbphotos</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
<span class="k">if</span> <span class="n">photo</span><span class="p">[</span><span class="s2">"momentID"</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pk</span>
<span class="k">if</span> <span class="n">photo</span><span class="p">[</span><span class="s2">&quot;momentID&quot;</span><span class="p">]</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pk</span>
<span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_by_uuid</span><span class="p">(</span><span class="n">photo_uuids</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_photos</span>
<div class="viewcode-block" id="MomentInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.MomentInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Returns all moment info as dictionary"""</span>
<span class="sd">&quot;&quot;&quot;Returns all moment info as dictionary&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">"pk"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pk</span><span class="p">,</span>
<span class="s2">"location"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span>
<span class="s2">"title"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">"subtitle"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">subtitle</span><span class="p">,</span>
<span class="s2">"start_date"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">"end_date"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">"date"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">"modification_date"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">modification_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="s2">&quot;pk&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pk</span><span class="p">,</span>
<span class="s2">&quot;location&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span>
<span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
<span class="s2">&quot;subtitle&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">subtitle</span><span class="p">,</span>
<span class="s2">&quot;start_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;end_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">date</span> <span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">&quot;modification_date&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">modification_date</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">modification_date</span>
<span class="k">else</span> <span class="kc">None</span><span class="p">,</span>
<span class="s2">"photos"</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos</span><span class="p">,</span>
<span class="s2">&quot;photos&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos</span><span class="p">,</span>
<span class="p">}</span></div></div>
</pre></div>
</article>
@ -317,7 +318,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.personinfo - osxphotos 0.58.1 documentation</title>
<title>osxphotos.personinfo - osxphotos 0.61.0 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.58.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.58.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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">
@ -334,7 +334,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></div>
<span class="k">class</span> <span class="nc">FaceInfo</span><span class="p">:</span>
<div class="viewcode-block" id="FaceInfo"><a class="viewcode-back" href="../../reference.html#osxphotos.FaceInfo">[docs]</a><span class="k">class</span> <span class="nc">FaceInfo</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Info about a face in the Photos library&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
@ -472,7 +472,7 @@
<span class="k">return</span> <span class="n">MPRI_Reg_Rect</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">w</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">face_rect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="FaceInfo.face_rect"><a class="viewcode-back" href="../../reference.html#osxphotos.FaceInfo.face_rect">[docs]</a> <span class="k">def</span> <span class="nf">face_rect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Get face rectangle coordinates for current version of the associated image</span>
<span class="sd"> If image has been edited, rectangle applies to edited version, otherwise original version</span>
<span class="sd"> Coordinates in format and reference frame used by PIL</span>
@ -486,16 +486,16 @@
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_make_point</span><span class="p">((</span><span class="bp">self</span><span class="o">.</span><span class="n">center_x</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">center_y</span><span class="p">))</span>
<span class="n">x0</span><span class="p">,</span> <span class="n">y0</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">radius</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="n">radius</span>
<span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">radius</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">radius</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">x0</span><span class="p">,</span> <span class="n">y0</span><span class="p">),</span> <span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">)]</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">x0</span><span class="p">,</span> <span class="n">y0</span><span class="p">),</span> <span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">)]</span></div>
<span class="k">def</span> <span class="nf">roll_pitch_yaw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="FaceInfo.roll_pitch_yaw"><a class="viewcode-back" href="../../reference.html#osxphotos.FaceInfo.roll_pitch_yaw">[docs]</a> <span class="k">def</span> <span class="nf">roll_pitch_yaw</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Roll, pitch, yaw of face in radians as tuple&quot;&quot;&quot;</span>
<span class="n">info</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span>
<span class="n">roll</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;roll&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">else</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;roll&quot;</span><span class="p">]</span>
<span class="n">pitch</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;pitch&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">else</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;pitch&quot;</span><span class="p">]</span>
<span class="n">yaw</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;yaw&quot;</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span> <span class="k">else</span> <span class="n">info</span><span class="p">[</span><span class="s2">&quot;yaw&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="p">(</span><span class="n">roll</span><span class="p">,</span> <span class="n">pitch</span><span class="p">,</span> <span class="n">yaw</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">roll</span><span class="p">,</span> <span class="n">pitch</span><span class="p">,</span> <span class="n">yaw</span><span class="p">)</span></div>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@ -598,7 +598,7 @@
<span class="k">return</span> <span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">xr</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">yr</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="FaceInfo.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.FaceInfo.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns dict representation of class instance&quot;&quot;&quot;</span>
<span class="n">roll</span><span class="p">,</span> <span class="n">pitch</span><span class="p">,</span> <span class="n">yaw</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_pitch_yaw</span><span class="p">()</span>
<span class="k">return</span> <span class="p">{</span>
@ -633,11 +633,11 @@
<span class="s2">&quot;intrash&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">intrash</span><span class="p">,</span>
<span class="s2">&quot;lip_makeup_type&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">lip_makeup_type</span><span class="p">,</span>
<span class="s2">&quot;smile_type&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">smile_type</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span></div>
<span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="FaceInfo.json"><a class="viewcode-back" href="../../reference.html#osxphotos.FaceInfo.json">[docs]</a> <span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return JSON representation of FaceInfo instance&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">())</span>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">())</span></div>
<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">&quot;FaceInfo(uuid=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">, center_x=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">center_x</span><span class="si">}</span><span class="s2">, center_y = </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">center_y</span><span class="si">}</span><span class="s2">, size=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">size</span><span class="si">}</span><span class="s2">, person=</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">, asset_uuid=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">asset_uuid</span><span class="si">}</span><span class="s2">)&quot;</span>
@ -654,7 +654,7 @@
<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">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></div>
<span class="k">def</span> <span class="nf">rotate_image_point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">xmid</span><span class="p">,</span> <span class="n">ymid</span><span class="p">,</span> <span class="n">angle</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.photoexporter - osxphotos 0.60.7 documentation</title>
<title>osxphotos.photoexporter - osxphotos 0.62.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.60.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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,6 +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="../../API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -463,7 +464,51 @@
<div class="viewcode-block" id="ExportResults"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults">[docs]</a><span class="k">class</span> <span class="nc">ExportResults</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Results class which holds export results for export&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot;Results class which holds export results for export</span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> converted_to_jpeg: list of files converted to jpeg</span>
<span class="sd"> deleted_directories: list of directories deleted</span>
<span class="sd"> deleted_files: list of files deleted</span>
<span class="sd"> error: list of tuples of (filename, error) for any errors generated during export</span>
<span class="sd"> exif_updated: list of files where exif data was updated with exiftool</span>
<span class="sd"> exiftool_error: list of tuples of (filename, error) for any errors generated by exiftool</span>
<span class="sd"> exiftool_warning: list of tuples of (filename, warning) for any warnings generated by exiftool</span>
<span class="sd"> exported: list of files exported</span>
<span class="sd"> exported_album: list of tuples of (file, album) for any files exported to an album</span>
<span class="sd"> metadata_changed: list of filenames that had metadata changes since last export</span>
<span class="sd"> missing: list of files that were missing</span>
<span class="sd"> missing_album: list of tuples of (file, album) for any files that were missing from an album</span>
<span class="sd"> new: list of files that were new</span>
<span class="sd"> aae_written: list of files where .AAE file was written</span>
<span class="sd"> sidecar_exiftool_skipped: list of files where exiftool sidecar was skipped</span>
<span class="sd"> sidecar_exiftool_written: list of files where exiftool sidecar was written</span>
<span class="sd"> sidecar_json_skipped: list of files where json sidecar was skipped</span>
<span class="sd"> sidecar_json_written: list of files where json sidecar was written</span>
<span class="sd"> sidecar_xmp_skipped: list of files where xmp sidecar was skipped</span>
<span class="sd"> sidecar_xmp_written: list of files where xmp sidecar was written</span>
<span class="sd"> sidecar_user_written: list of files where user sidecar was written</span>
<span class="sd"> sidecar_user_skipped: list of files where user sidecar was skipped</span>
<span class="sd"> sidecar_user_error: list of tuples of (filename, error) for any errors generated by user sidecar</span>
<span class="sd"> skipped: list of files that were skipped</span>
<span class="sd"> skipped_album: list of tuples of (file, album) for any files that were skipped from an album</span>
<span class="sd"> to_touch: list of files that were touched</span>
<span class="sd"> touched: list of files that were touched</span>
<span class="sd"> updated: list of files that were updated</span>
<span class="sd"> xattr_skipped: list of files where xattr was skipped</span>
<span class="sd"> xattr_written: list of files where xattr was written</span>
<span class="sd"> user_written: list of files written by user post_function</span>
<span class="sd"> user_skipped: list of files skipped by user post_function</span>
<span class="sd"> user_error: list of tuples of (filename, error) for any errors generated by user post_function</span>
<span class="sd"> Notes:</span>
<span class="sd"> Each attribute is a list of files or None if no files for that attribute.</span>
<span class="sd"> Error and warning attributes are a list of tuples of (filename, error) where filename is the file that caused the error and error is the error message.</span>
<span class="sd"> Album attributes are a list of tuples of (file, album) where file is the file exported and album is the album it was exported to.</span>
<span class="sd"> ExportResults can be added together with the += operator to combine results as the export progresses.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># Note: __init__ docs above added in the class docstring so they are picked up by sphinx</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;_datetime&quot;</span><span class="p">,</span>
@ -487,6 +532,9 @@
<span class="s2">&quot;sidecar_json_written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_xmp_skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_xmp_written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_user_written&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_user_skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;sidecar_user_error&quot;</span><span class="p">,</span>
<span class="s2">&quot;skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;skipped_album&quot;</span><span class="p">,</span>
<span class="s2">&quot;to_touch&quot;</span><span class="p">,</span>
@ -494,38 +542,51 @@
<span class="s2">&quot;updated&quot;</span><span class="p">,</span>
<span class="s2">&quot;xattr_skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;xattr_written&quot;</span><span class="p">,</span>
<span class="s2">&quot;user_written&quot;</span><span class="p">,</span>
<span class="s2">&quot;user_skipped&quot;</span><span class="p">,</span>
<span class="s2">&quot;user_error&quot;</span><span class="p">,</span>
<span class="p">]</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">converted_to_jpeg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">deleted_directories</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">deleted_files</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exif_updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_warning</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exported</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">exported_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">metadata_changed</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">missing</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">new</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">aae_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_json_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">to_touch</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">touched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">converted_to_jpeg</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">deleted_directories</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">deleted_files</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">error</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">exif_updated</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_error</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">exiftool_warning</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">exported</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">exported_album</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">metadata_changed</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">missing</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">missing_album</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">new</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">aae_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_exiftool_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_json_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_json_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_xmp_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_user_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_user_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">sidecar_user_error</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">skipped_album</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">to_touch</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">touched</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">updated</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">xattr_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">xattr_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">user_written</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">user_skipped</span><span class="p">:</span> <span class="nb">list</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">user_error</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">tuple</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">&quot;&quot;&quot;ExportResults data class to hold results of export.</span>
<span class="sd"> See class docstring for details.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">local_vars</span> <span class="o">=</span> <span class="nb">locals</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
<span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="p">:</span>
@ -558,11 +619,17 @@
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_user_written</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_user_skipped</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">missing</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_written</span>
<span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_skipped</span>
<span class="p">)</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span><span class="p">]</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span><span class="p">]</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">]</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">sidecar_user_error</span><span class="p">]</span>
<span class="n">files</span> <span class="o">+=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">user_error</span><span class="p">]</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">files</span><span class="p">))</span></div>
@ -1630,7 +1697,8 @@
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">export_as_hardlink</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="n">aae_dest</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span> <span class="ow">and</span> <span class="nb">any</span><span class="p">(</span>
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">]):</span>
<span class="p">[</span><span class="n">options</span><span class="o">.</span><span class="n">overwrite</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">force_update</span><span class="p">]</span>
<span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">options</span><span class="o">.</span><span class="n">fileutil</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">aae_dest</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>

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.60.7 documentation</title>
<title>osxphotos.photoinfo - osxphotos 0.62.0 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.60.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.0 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -262,6 +263,8 @@
<span class="kn">from</span> <span class="nn">.query_builder</span> <span class="kn">import</span> <span class="n">get_query</span>
<span class="kn">from</span> <span class="nn">.scoreinfo</span> <span class="kn">import</span> <span class="n">ScoreInfo</span>
<span class="kn">from</span> <span class="nn">.searchinfo</span> <span class="kn">import</span> <span class="n">SearchInfo</span>
<span class="kn">from</span> <span class="nn">.shareinfo</span> <span class="kn">import</span> <span class="n">ShareInfo</span><span class="p">,</span> <span class="n">get_moment_share_info</span><span class="p">,</span> <span class="n">get_share_info</span>
<span class="kn">from</span> <span class="nn">.shareparticipant</span> <span class="kn">import</span> <span class="n">ShareParticipant</span><span class="p">,</span> <span class="n">get_share_participants</span>
<span class="kn">from</span> <span class="nn">.uti</span> <span class="kn">import</span> <span class="n">get_preferred_uti_extension</span><span class="p">,</span> <span class="n">get_uti_for_extension</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">_get_resource_loc</span><span class="p">,</span> <span class="n">hexdigest</span><span class="p">,</span> <span class="n">list_directory</span>
@ -370,6 +373,16 @@
<span class="sd">&quot;&quot;&quot;Returns candidate path for original photo on Photos &gt;= version 5&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;shared&quot;</span><span class="p">]:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_5_shared</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shared_moment</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">7</span>
<span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_shared_moment</span><span class="p">()</span>
<span class="p">):</span>
<span class="c1"># path for photos in shared moments if it&#39;s in the shared moment folder</span>
<span class="c1"># the file may also be in the originals folder which the next check will catch</span>
<span class="c1"># check shared_moment first as a photo can be both a shared moment and syndicated</span>
<span class="c1"># and if so, will be in the shared moment folder</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_shared_moment</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
<span class="c1"># path for &quot;shared with you&quot; syndicated photos that have not yet been saved to the library</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_syndication</span><span class="p">()</span>
@ -413,8 +426,8 @@
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_path_syndication</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for syndicated photo on Photos &gt;= version 8&quot;&quot;&quot;</span>
<span class="c1"># Photos 8+ stores syndicated photos in a separate directory</span>
<span class="sd">&quot;&quot;&quot;Return path for syndicated photo on Photos &gt;= version 7&quot;&quot;&quot;</span>
<span class="c1"># Photos 7+ stores syndicated photos in a separate directory</span>
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID.ext</span>
<span class="c1"># where X is first digit of UUID</span>
<span class="n">syndication_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/syndication/originals&quot;</span>
@ -427,6 +440,21 @@
<span class="p">)</span>
<span class="k">return</span> <span class="n">path</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">_path_shared_moment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for shared moment photo on Photos &gt;= version 7&quot;&quot;&quot;</span>
<span class="c1"># Photos 7+ stores shared moment photos in a separate directory</span>
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/momentshared/originals/X/UUID.ext</span>
<span class="c1"># where X is first digit of UUID</span>
<span class="n">momentshared_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/momentshared/originals&quot;</span>
<span class="n">uuid_dir</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="n">momentshared_path</span><span class="p">,</span>
<span class="n">uuid_dir</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">path</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">_path_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Returns candidate path for original photo on Photos &lt;= version 4&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;has_raw&quot;</span><span class="p">]:</span>
@ -547,7 +575,7 @@
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_path_edited_4</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;return path_edited for Photos &lt;= 4; modified version of code in PhotoInfo to debug #859&quot;&quot;&quot;</span>
<span class="sd">&quot;&quot;&quot;return path_edited for Photos &lt;= 4; #859&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;hasAdjustments&quot;</span><span class="p">]:</span>
<span class="k">return</span> <span class="kc">None</span>
@ -652,7 +680,7 @@
<span class="c1"># In Photos 5, raw is in same folder as original but with _4.ext</span>
<span class="c1"># Unless &quot;Copy Items to the Photos Library&quot; is not checked</span>
<span class="c1"># then RAW image is not renamed but has same name is jpeg buth with raw extension</span>
<span class="c1"># then RAW image is not renamed but has same name is jpeg but with raw extension</span>
<span class="c1"># Current implementation finds images with the correct raw UTI extension</span>
<span class="c1"># in same folder as the original and with same stem as original in form: original_stem*.raw_ext</span>
<span class="c1"># TODO: I don&#39;t like this -- would prefer a more deterministic approach but until I have more</span>
@ -722,6 +750,7 @@
<span class="sa">f</span><span class="s2">&quot;MISSING PATH: RAW photo for UUID </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="si">}</span><span class="s2"> should be at </span><span class="si">{</span><span class="n">photopath</span><span class="si">}</span><span class="s2"> but does not appear to exist&quot;</span>
<span class="p">)</span>
<span class="n">photopath</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">return</span> <span class="n">photopath</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">description</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@ -1107,6 +1136,8 @@
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">live_photo</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">ismissing</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_photo_shared_5</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared_moment</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">7</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_shared_moment</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
<span class="c1"># syndicated (&quot;Shared with you&quot;) photos not yet saved to library</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_live_syndicated</span><span class="p">()</span>
@ -1169,8 +1200,8 @@
<span class="k">return</span> <span class="n">photopath</span>
<span class="k">def</span> <span class="nf">_path_live_syndicated</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for live syndicated photo on Photos &gt;= version 8&quot;&quot;&quot;</span>
<span class="c1"># Photos 8+ stores live syndicated photos in a separate directory</span>
<span class="sd">&quot;&quot;&quot;Return path for live syndicated photo on Photos &gt;= version 7&quot;&quot;&quot;</span>
<span class="c1"># Photos 7+ stores live syndicated photos in a separate directory</span>
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID_3.mov</span>
<span class="c1"># where X is first digit of UUID</span>
<span class="n">syndication_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/syndication/originals&quot;</span>
@ -1184,6 +1215,22 @@
<span class="p">)</span>
<span class="k">return</span> <span class="n">live_photo</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">live_photo</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">_path_live_shared_moment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Return path for live shared moment photo on Photos &gt;= version 7&quot;&quot;&quot;</span>
<span class="c1"># Photos 7+ stores live shared moment photos in a separate directory</span>
<span class="c1"># in ~/Photos Library.photoslibrary/scopes/momentshared/originals/X/UUID_3.mov</span>
<span class="c1"># where X is first digit of UUID</span>
<span class="n">shared_moment_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/momentshared/originals&quot;</span>
<span class="n">uuid_dir</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span><span class="si">}</span><span class="s2">_3.mov&quot;</span>
<span class="n">live_photo</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">,</span>
<span class="n">shared_moment_path</span><span class="p">,</span>
<span class="n">uuid_dir</span><span class="p">,</span>
<span class="n">filename</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">live_photo</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">live_photo</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">path_derivatives</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)&quot;&quot;&quot;</span>
@ -1194,19 +1241,29 @@
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_5_shared</span><span class="p">()</span>
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared_moment</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">7</span><span class="p">:</span>
<span class="c1"># shared moments</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/momentshared/resources/derivatives&quot;</span>
<span class="n">thumb_path</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">derivative_path</span><span class="si">}</span><span class="s2">/masters/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">_4_5005_c.jpeg&quot;</span>
<span class="p">)</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">:</span>
<span class="c1"># syndicated (&quot;Shared with you&quot;) photos not yet saved to library</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="s2">&quot;scopes/syndication/resources/derivatives&quot;</span>
<span class="n">thumb_path</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">derivative_path</span><span class="si">}</span><span class="s2">/masters/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">_4_5005_c.jpeg&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;resources/derivatives/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="s2">&quot;resources/derivatives&quot;</span>
<span class="n">thumb_path</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;resources/derivatives/masters/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">_4_5005_c.jpeg&quot;</span>
<span class="p">)</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">derivative_path</span><span class="p">)</span>
<span class="n">derivative_path</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
<span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">derivative_path</span><span class="p">)</span>
<span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">directory</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">thumb_path</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span><span class="o">.</span><span class="n">joinpath</span><span class="p">(</span><span class="n">thumb_path</span><span class="p">)</span>
<span class="c1"># find all files that start with uuid in derivative path</span>
@ -1546,9 +1603,9 @@
<span class="k">def</span> <span class="nf">syndicated</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return true if photo was shared via syndication (e.g. via Messages, etc.);</span>
<span class="sd"> these are photos that appear in &quot;Shared with you&quot; album.</span>
<span class="sd"> Photos 8+ only; returns None if not Photos 8+.</span>
<span class="sd"> Photos 7+ only; returns None if not Photos 7+.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">7</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
@ -1563,10 +1620,10 @@
<span class="k">def</span> <span class="nf">saved_to_library</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Return True if syndicated photo has been saved to library;</span>
<span class="sd"> returns False if photo is not syndicated or has not been saved to the library.</span>
<span class="sd"> Returns None if not Photos 8+.</span>
<span class="sd"> Syndicated photos are photos that appear in &quot;Shared with you&quot; album; Photos 8+ only.</span>
<span class="sd"> Returns None if not Photos 7+.</span>
<span class="sd"> Syndicated photos are photos that appear in &quot;Shared with you&quot; album; Photos 7+ only.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">7</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
@ -1574,6 +1631,46 @@
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">shared_moment</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is part of a shared moment otherwise False (Photos 7+ only)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;moment_share&quot;</span><span class="p">])</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">shared_moment_info</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ShareInfo</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns ShareInfo object with information about the shared moment the photo is part of (Photos 7+ only)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">7</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">get_moment_share_info</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">share_info</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ShareInfo</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns ShareInfo object with information about the shared photo in a shared iCloud library (Photos 8+ only) (currently experimental)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">get_share_info</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">shared_library</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns True if photo is in a shared iCloud library otherwise False (Photos 8+ only)&quot;&quot;&quot;</span>
<span class="c1"># TODO: this is just a guess right now as I don&#39;t currently use shared libraries</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">&quot;active_library_participation_state&quot;</span><span class="p">])</span>
<span class="nd">@cached_property</span>
<span class="k">def</span> <span class="nf">share_participants</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="n">ShareParticipant</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;Returns list of ShareParticpant objects with information on who the photo is shared with (Photos 8+ only)&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[]</span>
<span class="k">return</span> <span class="n">get_share_participants</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">labels</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;returns list of labels applied to photo by Photos image categorization</span>
@ -2076,6 +2173,10 @@
<span class="n">place</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">place</span> <span class="k">else</span> <span class="p">{}</span>
<span class="n">score</span> <span class="o">=</span> <span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">score</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">score</span> <span class="k">else</span> <span class="p">{}</span>
<span class="c1"># do not add any new properties to data_dict as this is used by export to determine</span>
<span class="c1"># if a photo needs to be re-exported and adding new properties may cause all photos</span>
<span class="c1"># to be re-exported</span>
<span class="c1"># see below `if not shallow:`</span>
<span class="n">dict_data</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;albums&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">albums</span><span class="p">,</span>
<span class="s2">&quot;burst&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">burst</span><span class="p">,</span>
@ -2150,6 +2251,7 @@
<span class="p">}</span>
<span class="c1"># non-shallow keys</span>
<span class="c1"># add any new properties here</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">shallow</span><span class="p">:</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;album_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">album</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span> <span class="k">for</span> <span class="n">album</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">album_info</span><span class="p">]</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;path_derivatives&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">path_derivatives</span>
@ -2177,6 +2279,10 @@
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">search_info_normalized</span>
<span class="k">else</span> <span class="p">{}</span>
<span class="p">)</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;syndicated&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">syndicated</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;saved_to_library&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">saved_to_library</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;shared_moment&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared_moment</span>
<span class="n">dict_data</span><span class="p">[</span><span class="s2">&quot;shared_library&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared_library</span>
<span class="k">return</span> <span class="n">dict_data</span></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.photosalbum - osxphotos 0.60.5 documentation</title>
<title>osxphotos.photosalbum - osxphotos 0.61.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.photosdb._photosdb_process_comments - osxphotos 0.60.5 documentation</title>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.61.0 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.60.5 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.61.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.photosdb.photosdb - osxphotos 0.60.5 documentation</title>
<title>osxphotos.photosdb.photosdb - osxphotos 0.62.0 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.60.5 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.62.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -259,7 +260,11 @@
<span class="kn">from</span> <span class="nn">..sqlite_utils</span> <span class="kn">import</span> <span class="n">sqlite_db_is_locked</span><span class="p">,</span> <span class="n">sqlite_open_ro</span>
<span class="kn">from</span> <span class="nn">..unicode</span> <span class="kn">import</span> <span class="n">normalize_unicode</span>
<span class="kn">from</span> <span class="nn">..utils</span> <span class="kn">import</span> <span class="n">_check_file_exists</span><span class="p">,</span> <span class="n">get_last_library_path</span><span class="p">,</span> <span class="n">noop</span>
<span class="kn">from</span> <span class="nn">.photosdb_utils</span> <span class="kn">import</span> <span class="n">get_db_model_version</span><span class="p">,</span> <span class="n">get_db_version</span>
<span class="kn">from</span> <span class="nn">.photosdb_utils</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">get_db_version</span><span class="p">,</span>
<span class="n">get_model_version</span><span class="p">,</span>
<span class="n">get_photos_version_from_model</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">is_macos</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
@ -288,6 +293,7 @@
<span class="n">labels_normalized</span><span class="p">,</span>
<span class="n">labels_normalized_as_dict</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">._photosdb_process_shared_library</span> <span class="kn">import</span> <span class="n">_process_shared_library_info</span>
<span class="kn">from</span> <span class="nn">._photosdb_process_syndicationinfo</span> <span class="kn">import</span> <span class="n">_process_syndicationinfo</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
@ -483,7 +489,7 @@
<span class="c1"># Dict to hold data on imports for Photos &lt;= 4</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_import_group</span> <span class="o">=</span> <span class="p">{}</span>
<span class="c1"># Dict to hold syndication info for Photos &gt;= 8</span>
<span class="c1"># Dict to hold syndication info for Photos &gt;= 7</span>
<span class="c1"># key is UUID and value is dict of syndication info</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_db_syndication_uuid</span> <span class="o">=</span> <span class="p">{}</span>
@ -523,7 +529,7 @@
<span class="c1"># photoanalysisd sometimes maintains this lock even after Photos is closed</span>
<span class="c1"># In those cases, make a temp copy of the file for sqlite3 to read</span>
<span class="k">if</span> <span class="n">sqlite_db_is_locked</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile</span><span class="p">):</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Database locked, creating temporary copy.&quot;</span><span class="p">)</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Database locked, creating temporary copy.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_copy_db_file</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile</span><span class="p">)</span>
<span class="c1"># _db_version is set from photos.db</span>
@ -538,21 +544,23 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">=</span> <span class="mi">4</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">=</span> <span class="mi">5</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_model_ver</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># only set for Photos 5+</span>
<span class="c1"># If Photos &gt;= 5, actual data isn&#39;t in photos.db but in Photos.sqlite</span>
<span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db_version</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">int</span><span class="p">(</span><span class="n">_PHOTOS_4_VERSION</span><span class="p">):</span>
<span class="n">dbpath</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span>
<span class="n">dbfile</span> <span class="o">=</span> <span class="n">dbpath</span> <span class="o">/</span> <span class="s2">&quot;Photos.sqlite&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_check_file_exists</span><span class="p">(</span><span class="n">dbfile</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;dbfile </span><span class="si">{</span><span class="n">dbfile</span><span class="si">}</span><span class="s2"> does not exist&quot;</span><span class="p">,</span> <span class="n">dbfile</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span> <span class="o">=</span> <span class="n">dbfile</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Processing database </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># if database is exclusively locked, make a copy of it and use the copy</span>
<span class="k">if</span> <span class="n">sqlite_db_is_locked</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">):</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Database locked, creating temporary copy.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_copy_db_file</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span> <span class="o">=</span> <span class="n">dbfile</span>
<span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Processing database </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_filepath</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># if database is exclusively locked, make a copy of it and use the copy</span>
<span class="k">if</span> <span class="n">sqlite_db_is_locked</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">):</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Database locked, creating temporary copy.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_copy_db_file</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="p">)</span>
<span class="c1"># set the photos version to actual value based on Photos.sqlite</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">=</span> <span class="n">get_db_model_version</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_photos_ver</span> <span class="o">=</span> <span class="n">get_photos_version_from_model</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_model_ver</span> <span class="o">=</span> <span class="n">get_model_version</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_tmp_db</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;_dbfile = </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile</span><span class="si">}</span><span class="s2">, _dbfile_actual = </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_dbfile_actual</span><span class="si">}</span><span class="s2">&quot;</span>
@ -1432,6 +1440,9 @@
<span class="c1"># photos 5+ only, for shared photos</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;cloudownerhashedpersonid&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># photos 7+ only, shared moments</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;moment_share&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># compute signatures for finding possible duplicates</span>
<span class="n">signature</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_duplicate_signature</span><span class="p">(</span><span class="n">uuid</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
@ -1744,6 +1755,12 @@
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;UTI_raw&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;raw_pair_info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># placeholders for shared library info on Photos 8+</span>
<span class="k">for</span> <span class="n">uuid</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dbphotos</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;active_library_participation_state&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;library_scope_share_state&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;library_scope&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># done with the database connection</span>
<span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
@ -1903,6 +1920,7 @@
<span class="c1"># get info on keyface -- some photos have null keyface so can&#39;t do a single query</span>
<span class="c1"># (at least not with my SQL skills)</span>
<span class="n">asset_fk</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photos_ver</span><span class="p">][</span><span class="s2">&quot;DETECTED_FACE_ASSET_FK&quot;</span><span class="p">]</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot; SELECT</span>
<span class="s2"> ZPERSON.Z_PK,</span>
@ -1911,7 +1929,7 @@
<span class="s2"> ZDETECTEDFACE.ZUUID</span>
<span class="s2"> FROM ZPERSON, ZDETECTEDFACE, </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"></span>
<span class="s2"> WHERE ZDETECTEDFACE.Z_PK = ZPERSON.ZKEYFACE AND</span>
<span class="s2"> ZDETECTEDFACE.ZASSET = </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_fk</span><span class="si">}</span><span class="s2"> = </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="p">)</span>
@ -1930,13 +1948,14 @@
<span class="c1"># get information on detected faces</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Processing detected faces in photos.&quot;</span><span class="p">)</span>
<span class="n">person_fk</span> <span class="o">=</span> <span class="n">_DB_TABLE_NAMES</span><span class="p">[</span><span class="n">photos_ver</span><span class="p">][</span><span class="s2">&quot;DETECTED_FACE_PERSON_FK&quot;</span><span class="p">]</span>
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;&quot;&quot; SELECT</span>
<span class="s2"> ZPERSON.Z_PK,</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID</span>
<span class="s2"> FROM ZPERSON, ZDETECTEDFACE, </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"></span>
<span class="s2"> WHERE ZDETECTEDFACE.ZPERSON = ZPERSON.Z_PK AND</span>
<span class="s2"> ZDETECTEDFACE.ZASSET = </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK</span>
<span class="s2"> WHERE </span><span class="si">{</span><span class="n">person_fk</span><span class="si">}</span><span class="s2"> = ZPERSON.Z_PK AND</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_fk</span><span class="si">}</span><span class="s2"> = </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK</span>
<span class="s2"> &quot;&quot;&quot;</span>
<span class="p">)</span>
@ -2144,7 +2163,8 @@
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZSAVEDASSETTYPE,</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZADDEDDATE,</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK,</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZCLOUDOWNERHASHEDPERSONID</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZCLOUDOWNERHASHEDPERSONID,</span>
<span class="s2"> </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZMOMENTSHARE</span>
<span class="s2"> FROM </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2"> </span>
<span class="s2"> JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.Z_PK </span>
<span class="s2"> ORDER BY </span><span class="si">{</span><span class="n">asset_table</span><span class="si">}</span><span class="s2">.ZUUID &quot;&quot;&quot;</span>
@ -2195,6 +2215,7 @@
<span class="c1"># 41 ZGENERICASSET.ZADDEDDATE -- date item added to the library</span>
<span class="c1"># 42 ZGENERICASSET.Z_PK -- primary key</span>
<span class="c1"># 43 ZGENERICASSET.ZCLOUDOWNERHASHEDPERSONID -- used to look up owner name (for shared photos)</span>
<span class="c1"># 44 ZASSET.ZMOMENTSHARE -- FK for ZSHARE (shared moments, Photos 5+; in Photos 7+ these are in the scopes/momentshared folder)</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">c</span><span class="p">:</span>
<span class="n">uuid</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
@ -2382,6 +2403,8 @@
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;pk&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">42</span><span class="p">]</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;cloudownerhashedpersonid&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">43</span><span class="p">]</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;moment_share&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">row</span><span class="p">[</span><span class="mi">44</span><span class="p">]</span>
<span class="c1"># initialize import session info which will be filled in later</span>
<span class="c1"># not every photo has an import session so initialize all records now</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;import_session&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
@ -2406,6 +2429,11 @@
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;UTI_edited_photo&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;UTI_edited_video&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># placeholder for shared library info (Photos 8+)</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;active_library_participation_state&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;library_scope_share_state&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">info</span><span class="p">[</span><span class="s2">&quot;library_scope&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="o">=</span> <span class="n">info</span>
<span class="c1"># compute signatures for finding possible duplicates</span>
@ -2714,10 +2742,14 @@
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Processing moments.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_moments</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">8</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">7</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Processing syndication info.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_syndicationinfo</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photos_version</span> <span class="o">&gt;=</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Processing shared iCloud library info&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_process_shared_library_info</span><span class="p">()</span>
<span class="n">verbose</span><span class="p">(</span><span class="s2">&quot;Done processing details from Photos library.&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_process_moments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@ -3727,6 +3759,16 @@
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_saved_to_library</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">syndicated</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">saved_to_library</span><span class="p">]</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">shared_moment</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">shared_moment</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_shared_moment</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">shared_moment</span><span class="p">]</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">shared_library</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">shared_library</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_shared_library</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">shared_library</span><span class="p">]</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">function</span><span class="p">:</span>
<span class="k">for</span> <span class="n">function</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">function</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="n">function</span><span class="p">[</span><span class="mi">0</span><span class="p">](</span><span class="n">photos</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.phototemplate - osxphotos 0.60.0 documentation</title>
<title>osxphotos.phototemplate - osxphotos 0.62.0 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.60.0 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.0 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.60.0 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -661,7 +662,7 @@
<span class="sd"> ([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">template</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="nb">str</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">template</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;template must be type str, not </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">template</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">options</span>
@ -1592,7 +1593,7 @@
<span class="c1"># if no uuid, then template is being validated but not actually run</span>
<span class="c1"># so don&#39;t run the function</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">elif</span> <span class="n">caller</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;export&quot;</span><span class="p">,</span> <span class="s2">&quot;query&quot;</span><span class="p">]:</span>
<span class="k">elif</span> <span class="n">caller</span> <span class="ow">in</span> <span class="p">{</span><span class="s2">&quot;export&quot;</span><span class="p">,</span> <span class="s2">&quot;query&quot;</span><span class="p">}:</span>
<span class="c1"># function signature is:</span>
<span class="c1"># def example(photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs) -&gt; Union[List, str]:</span>
<span class="n">values</span> <span class="o">=</span> <span class="n">template_func</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="n">field_arg</span><span class="p">)</span>
@ -1608,7 +1609,7 @@
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Invalid return type for function </span><span class="si">{</span><span class="n">funcname</span><span class="si">}</span><span class="s2">: expected str or list&quot;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">str</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="n">values</span><span class="p">]</span>
<span class="c1"># sanitize directory names if needed</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.placeinfo - osxphotos 0.60.7 documentation</title>
<title>osxphotos.placeinfo - osxphotos 0.61.0 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.60.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.queryoptions - osxphotos 0.60.5 documentation</title>
<title>osxphotos.queryoptions - osxphotos 0.62.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.62.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.0 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,6 +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="../../api_readme.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -309,6 +310,10 @@
<span class="sd"> not_syndicated: search for photos that have not been shared via syndication (&quot;Shared with You&quot; album via Messages, etc.)</span>
<span class="sd"> saved_to_library: search for syndicated photos that have been saved to the Photos library</span>
<span class="sd"> not_saved_to_library: search for syndicated photos that have not been saved to the Photos library</span>
<span class="sd"> shared_moment: search for photos that have been shared via a shared moment</span>
<span class="sd"> not_shared_moment: search for photos that have not been shared via a shared moment</span>
<span class="sd"> shared_library: search for photos that are part of a shared iCloud library</span>
<span class="sd"> not_shared_library: search for photos that are not part of a shared iCloud library</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">added_after</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
@ -397,6 +402,10 @@
<span class="n">not_syndicated</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">saved_to_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_saved_to_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">shared_moment</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_shared_moment</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">shared_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_shared_library</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</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="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
@ -468,7 +477,10 @@
<span class="p">(</span><span class="s2">&quot;deleted_only&quot;</span><span class="p">,</span> <span class="s2">&quot;not_deleted&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;syndicated&quot;</span><span class="p">,</span> <span class="s2">&quot;not_syndicated&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;saved_to_library&quot;</span><span class="p">,</span> <span class="s2">&quot;not_saved_to_library&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;shared_moment&quot;</span><span class="p">,</span> <span class="s2">&quot;not_shared_moment&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;shared_library&quot;</span><span class="p">,</span> <span class="s2">&quot;not_shared_library&quot;</span><span class="p">),</span>
<span class="p">]</span>
<span class="c1"># TODO: add option to validate requiring at least one query arg</span>
<span class="k">for</span> <span class="n">arg</span><span class="p">,</span> <span class="n">not_arg</span> <span class="ow">in</span> <span class="n">exclusive</span><span class="p">:</span>
<span class="k">if</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="ow">and</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">not_arg</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.scoreinfo - osxphotos 0.60.5 documentation</title>
<title>osxphotos.scoreinfo - osxphotos 0.61.0 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.60.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.60.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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.searchinfo - osxphotos 0.58.1 documentation</title>
<title>osxphotos.searchinfo - osxphotos 0.61.0 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.58.1 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.61.0 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.58.1 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.61.0 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">

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ Welcome to OSXPhotos's documentation!
cli
template_help
package_overview
API_README
reference

View File

@ -162,9 +162,9 @@ e.g. ``"{created.year}/{openbrace}{title}{closebrace}"`` would result in ``"2020
**Variables**
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}``. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}`` where ``VALUE`` is a template statement. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
Variables can also be referenced as fields in the template string, for example: ``{var:year,created.year}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
Variables can also be referenced as fields in the template string, for example: ``{var:year,{created.year}}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
If you need to use a ``%`` (percent sign character), you can escape the percent sign by using ``%%``. You can also use the ``{percent}`` template field where a template field is required. For example:
@ -361,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.60.7'
- The osxphotos version, e.g. '0.62.1'
* - {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.60.7',
VERSION: '0.62.1',
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.60.7 documentation</title>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,7 @@
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -740,6 +741,30 @@ See <cite>osxphotos help timewarp</cite> for more information.</p>
<dd><p>Search for syndicated photos that have not saved to the library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-shared-moment">
<span class="sig-name descname"><span class="pre">--shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-not-shared-moment">
<span class="sig-name descname"><span class="pre">--not-shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-not-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-shared-library">
<span class="sig-name descname"><span class="pre">--shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-not-shared-library">
<span class="sig-name descname"><span class="pre">--not-shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-not-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-add-locations-regex">
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-add-locations-regex" title="Permalink to this definition">#</a></dt>
@ -1695,6 +1720,30 @@ to modify this behavior.</p>
<dd><p>Search for syndicated photos that have not saved to the library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-shared-moment">
<span class="sig-name descname"><span class="pre">--shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-not-shared-moment">
<span class="sig-name descname"><span class="pre">--not-shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-not-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-shared-library">
<span class="sig-name descname"><span class="pre">--shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-not-shared-library">
<span class="sig-name descname"><span class="pre">--not-shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-not-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-regex">
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-regex" title="Permalink to this definition">#</a></dt>
@ -1918,6 +1967,12 @@ to modify this behavior.</p>
<dd><p>Drop the photos extension when naming sidecar files. By default, sidecar files are named in format photo_filename.photo_ext.sidecar_ext, e.g. IMG_1234.JPG.xmp. Use sidecar-drop-ext to ignore the photo extension. Resulting sidecar files will have name in format IMG_1234.xmp. Warning: this may result in sidecar filename collisions if there are files of different types but the same name in the output directory, e.g. IMG_1234.JPG and IMG_1234.MOV.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-sidecar-template">
<span class="sig-name descname"><span class="pre">--sidecar-template</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;MAKO_TEMPLATE_FILE</span> <span class="pre">SIDECAR_FILENAME_TEMPLATE</span> <span class="pre">OPTIONS&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-sidecar-template" title="Permalink to this definition">#</a></dt>
<dd><p>Create a custom sidecar file for each photo exported with user provided Mako template (MAKO_TEMPLATE_FILE). MAKO_TEMPLATE_FILE must be a valid Mako template (see <a class="reference external" href="https://www.makotemplates.org/">https://www.makotemplates.org/</a>). The template will passed the following variables: photo (PhotoInfo object for the photo being exported), sidecar_path (pathlib.Path object for the path to the sidecar being written), and photo_path (pathlib.Path object for the path to the exported photo. SIDECAR_FILENAME_TEMPLATE must be a valid template string (see Templating System in help) which will be rendered to generate the filename of the sidecar file. The <cite>{filepath}</cite> template variable may be used in the SIDECAR_FILENAME_TEMPLATE to refer to the filename of the photo being exported. OPTIONS is a comma-separated list of strings providing additional options to the template. Valid options are: write_skipped, strip_whitespace, strip_lines, skip_zero, catch_errors, none. write_skipped will cause the sidecar file to be written even if the photo is skipped during export. If write_skipped is not passed as an option, the sidecar file will not be written if the photo is skipped during export. strip_whitespace and strip_lines indicate whether or not to strip whitespace and blank lines, respectively, from the resulting sidecar file. skip_zero causes the sidecar file to be skipped if the rendered template is zero-length. catch_errors causes errors in the template to be caught and logged but not raised. Without catch_errors, osxphotos will abort the export if an error occurs in the template. For example, to create a sidecar file with extension .xmp using a template file named sidecar.mako and write a sidecar for skipped photos and strip blank lines but not whitespace: <cite>sidecar-template sidecar.mako {filepath}.xmp write_skipped,strip_lines</cite>. To do the same but to drop the photo extension from the sidecar filename: <cite>sidecar-template sidecar.mako {filepath.parent}/{filepath.stem}.xmp write_skipped,strip_lines</cite>. If you are not passing any options, you must pass none as the last argument to sidecar-template: <cite>sidecar-template sidecar.mako {filepath}.xmp none</cite>. For an example Mako file see <a class="reference external" href="https://raw.githubusercontent.com/RhetTbull/osxphotos/main/examples/custom_sidecar.mako">https://raw.githubusercontent.com/RhetTbull/osxphotos/main/examples/custom_sidecar.mako</a></p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-exiftool">
<span class="sig-name descname"><span class="pre">--exiftool</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-exiftool" title="Permalink to this definition">#</a></dt>
@ -2076,13 +2131,13 @@ to modify this behavior.</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-cleanup">
<span class="sig-name descname"><span class="pre">--cleanup</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-cleanup" title="Permalink to this definition">#</a></dt>
<dd><p>Cleanup export directory by deleting any files which were not included in this export set. For example, photos which had previously been exported and were subsequently deleted in Photos. WARNING: cleanup will delete <em>any</em> files in the export directory that were not exported by osxphotos, for example, your own scripts or other files. Be sure this is what you intend before using cleanup. Use dry-run with cleanup first if youre not certain.</p>
<dd><p>Cleanup export directory by deleting any files which were not included in this export set. For example, photos which had previously been exported and were subsequently deleted in Photos. WARNING: cleanup will delete <em>any</em> files in the export directory that were not exported by osxphotos, for example, your own scripts or other files. Be sure this is what you intend before using cleanup. Use dry-run with cleanup first if youre not certain. To prevent files not generated by osxphotos from being deleted, you may specify one or more rulesin a file named <cite>.osxphotos_keep</cite> in the export directory. This file uses the same format as a .gitignore file and should contain one rule per line; lines starting with a <cite>#</cite> will be ignored. Reference <a class="reference external" href="https://git-scm.com/docs/gitignore#_pattern_format">https://git-scm.com/docs/gitignore#_pattern_format</a> for details. In addition to the standard .gitignore rules, the rules may also be the absolute path to a file or directory. For example if export destination is <cite>/Volumes/Photos</cite> and you want to keep all <cite>.txt</cite> files, in the top level of the export directory, you can specify <cite>/*.txt”</cite> in the .osxphotos_keep file. If you want to keep all <cite>.txt</cite> files in the export directory and all subdirectories, you can specify <cite>**/*.txt</cite>. If present, the .osxphotos_keep file will be read after the export is completed and any rules found in the file will be added to the list of rules to keep. See also keep.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-keep">
<span class="sig-name descname"><span class="pre">--keep</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;KEEP_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-keep" title="Permalink to this definition">#</a></dt>
<dd><p>When used with cleanup, prevents file or directory KEEP_PATH from being deleted when cleanup is run. Use this if there are files in the export directory that you dont want to be deleted when cleanup is run. KEEP_PATH may be a file path, e.g. /Volumes/Photos/keep.jpg, or a file path and wild card, e.g. /Volumes/Photos/<em>.txt, or a directory, e.g. /Volumes/Photos/KeepMe. KEEP_PATH may be an absolute path or a relative path. If it is relative, it must be relative to the export destination. For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, you can specify `keep “/Volumes/Photos/</em>.txt”` or <cite>keep “*.txt”</cite>. If wild card is used, KEEP_PATH must be enclosed in quotes to prevent the shell from expanding the wildcard, e.g. <cite>keep “/Volumes/Photos/*.txt”</cite>. If KEEP_PATH is a directory, all files and directories contained in KEEP_PATH will be kept. keep may be repeated to keep additional files/directories.</p>
<span class="sig-name descname"><span class="pre">--keep</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;KEEP_RULE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-keep" title="Permalink to this definition">#</a></dt>
<dd><p>When used with cleanup, prevents file or directory matching KEEP_RULE from being deleted when cleanup is run. Use this if there are files in the export directory that you dont want to be deleted when cleanup is run. KEEP_RULE follows the same format rules a .gitignore file. Reference <a class="reference external" href="https://git-scm.com/docs/gitignore#_pattern_format">https://git-scm.com/docs/gitignore#_pattern_format</a> for details. In addition to the standard .gitignore rules, KEEP_RULE may also be the absolute path to a file or directory. For example if export destination is <cite>/Volumes/Photos</cite> and you want to keep all <cite>.txt</cite> files, in the top level of the export directory, you can specify <cite>keep “/*.txt”</cite>. If you want to keep all <cite>.txt</cite> files in the export directory and all subdirectories, you can specify <cite>keep “**/*.txt”</cite>. If wild card is used, KEEP_RULE must be enclosed in quotes to prevent the shell from expanding the wildcard. keep may be repeated to keep additional files/directories. Rules may also be included in a file named <cite>.osxphotos_keep</cite> in the export directory. If present, this file will be read after the export is completed and any rules found in the file will be added to the list of rules to keep. This file uses the same format as a .gitignore file and should contain one rule per line; lines starting with a <cite>#</cite> will be ignored.</p>
</dd></dl>
<dl class="std option">
@ -2106,7 +2161,18 @@ to modify this behavior.</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-post-command">
<span class="sig-name descname"><span class="pre">--post-command</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;CATEGORY</span> <span class="pre">COMMAND&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-post-command" title="Permalink to this definition">#</a></dt>
<dd><p>Run COMMAND on exported files of category CATEGORY. CATEGORY can be one of: exported, new, updated, skipped, missing, exif_updated, touched, converted_to_jpeg, sidecar_json_written, sidecar_json_skipped, sidecar_exiftool_written, sidecar_exiftool_skipped, sidecar_xmp_written, sidecar_xmp_skipped, error. COMMAND is an osxphotos template string, for example: post-command exported “echo {filepath|shell_quote} &gt;&gt; {export_dir}/exported.txt”, which appends the full path of all exported files to the file exported.txt. You can run more than one command by repeating the post-command option with different arguments. See Post Command below.</p>
<dd><p>Run COMMAND on exported files of category CATEGORY. CATEGORY can be one of: exported, new, updated, skipped, missing, exif_updated, touched, converted_to_jpeg, sidecar_json_written, sidecar_json_skipped, sidecar_exiftool_written, sidecar_exiftool_skipped, sidecar_xmp_written, sidecar_xmp_skipped, error. COMMAND is an osxphotos template string, for example: post-command exported “echo {filepath|shell_quote} &gt;&gt; {export_dir}/exported.txt”, which appends the full path of all exported files to the file exported.txt. You can run more than one command by repeating the post-command option with different arguments. See also post-command-error and post-function.See Post Command below.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-post-command-error">
<span class="sig-name descname"><span class="pre">--post-command-error</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;ACTION&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-post-command-error" title="Permalink to this definition">#</a></dt>
<dd><p>Specify either <cite>continue</cite> or <cite>break</cite> for ACTION to control behavior when a post-command fails. If <cite>continue</cite>, osxphotos will log the error and continue processing. If <cite>break</cite>, osxphotos will stop processing any additional post-command commands for the current photo but will continue with the export. Without post-command-error, osxphotos will abort the export if a post-command encounters an error.</p>
<dl class="field-list simple">
<dt class="field-odd">Options<span class="colon">:</span></dt>
<dd class="field-odd"><p>continue | break</p>
</dd>
</dl>
</dd></dl>
<dl class="std option">
@ -3301,6 +3367,30 @@ If the same query option is provided multiple times, they are treated as
<dd><p>Search for syndicated photos that have not saved to the library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-shared-moment">
<span class="sig-name descname"><span class="pre">--shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-not-shared-moment">
<span class="sig-name descname"><span class="pre">--not-shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-not-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-shared-library">
<span class="sig-name descname"><span class="pre">--shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-not-shared-library">
<span class="sig-name descname"><span class="pre">--not-shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-not-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-regex">
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-query-regex" title="Permalink to this definition">#</a></dt>
@ -3862,6 +3952,30 @@ If the same query option is provided multiple times, they are treated as
<dd><p>Search for syndicated photos that have not saved to the library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-shared-moment">
<span class="sig-name descname"><span class="pre">--shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-not-shared-moment">
<span class="sig-name descname"><span class="pre">--not-shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-not-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-shared-library">
<span class="sig-name descname"><span class="pre">--shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-not-shared-library">
<span class="sig-name descname"><span class="pre">--not-shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-not-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-regex">
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-regex" title="Permalink to this definition">#</a></dt>
@ -4545,6 +4659,30 @@ two different computers, you can export the metadata to a shared folder.</p>
<dd><p>Search for syndicated photos that have not saved to the library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-shared-moment">
<span class="sig-name descname"><span class="pre">--shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-not-shared-moment">
<span class="sig-name descname"><span class="pre">--not-shared-moment</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-not-shared-moment" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared moment</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-shared-library">
<span class="sig-name descname"><span class="pre">--shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-not-shared-library">
<span class="sig-name descname"><span class="pre">--not-shared-library</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-sync-not-shared-library" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not part of a shared library</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-sync-regex">
<span class="sig-name descname"><span class="pre">--regex</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-sync-regex" title="Permalink to this definition">#</a></dt>
@ -4871,51 +5009,46 @@ See Timewarp Overview below for additional information.</p>
</section>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>Usage: python -m osxphotos [OPTIONS] COMMAND [ARGS]...
osxphotos: query and export your Photos library
osxphotos: the multi-tool for your Photos library
Options:
--db PHOTOS_LIBRARY_PATH Specify Photos database path. Path to Photos
library/database can be specified using either
--db or directly as PHOTOS_LIBRARY positional
argument. If neither --db or PHOTOS_LIBRARY
provided, will attempt to find the library to use
in the following order: 1. last opened library, 2.
system library, 3. ~/Pictures/Photos
Library.photoslibrary
--json Print output in JSON format.
-v, --version Show the version and exit.
-h, --help Show this message and exit.
-v, --version Show the version and exit.
-h, --help Show this message and exit.
Commands:
about Print information about osxphotos including license.
albums Print out albums found in the Photos library.
diff Compare two Photos databases and print out differences
docs Open osxphotos documentation in your browser.
dump Print list of all photos &amp; associated info from the Photos...
exiftool Run exiftool on previously exported files to update metadata.
export Export photos from the Photos database.
exportdb Utilities for working with the osxphotos export database
help Print help; for help on commands: help &lt;command&gt;.
import Import photos and videos into Photos.
info Print out descriptive info of the Photos library database.
inspect Interactively inspect photos selected in Photos.
install Install Python packages into the same environment as osxphotos
keywords Print out keywords found in the Photos library.
labels Print out image classification labels found in the Photos...
list Print list of Photos libraries found on the system.
orphans Find orphaned photos in a Photos library
persons Print out persons (faces) found in the Photos library.
places Print out places found in the Photos library.
query Query the Photos database using 1 or more search options; if...
repl Run interactive osxphotos REPL shell (useful for debugging,...
run Run a python file using same environment as osxphotos.
snap Create snapshot of Photos database to use with diff command
theme Manage osxphotos color themes.
timewarp Adjust date/time/timezone of photos in Apple Photos.
tutorial Display osxphotos tutorial.
uninstall Uninstall Python packages from the osxphotos environment
uuid Print out unique IDs (UUID) of photos selected in Photos
version Check for new version of osxphotos.
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 &amp; associated info from the...
exiftool Run exiftool on previously exported files to update...
export Export photos from the Photos database.
exportdb Utilities for working with the osxphotos export database
help Print help; for help on commands: help &lt;command&gt;.
import Import photos and videos into Photos.
info Print out descriptive info of the Photos library database.
inspect Interactively inspect photos selected in Photos.
install Install Python packages into the same environment as...
keywords Print out keywords found in the Photos library.
labels Print out image classification labels found in the...
list Print list of Photos libraries found on the system.
orphans Find orphaned photos in a Photos library
persons Print out persons (faces) found in the Photos library.
places Print out places found in the Photos library.
query Query the Photos database using 1 or more search...
repl Run interactive osxphotos REPL shell (useful for...
run Run a python file using same environment as osxphotos.
show Show photo, album, or folder in Photos from UUID_OR_NAME
snap Create snapshot of Photos database to use with diff command
sync Sync metadata and albums between Photos libraries.
theme Manage osxphotos color themes.
timewarp Adjust date/time/timezone of photos in Apple Photos.
tutorial Display osxphotos tutorial.
uninstall Uninstall Python packages from the osxphotos environment
uuid Print out unique IDs (UUID) of photos selected in Photos
version Check for new version of osxphotos.
</pre></div>
</div>
</section>

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.60.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.62.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" />
@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -159,6 +159,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -1601,8 +1602,6 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-favorite">osxphotos-sync command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-hdr
@ -1648,6 +1647,8 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-in-album">osxphotos-sync command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-incloud
@ -1794,6 +1795,36 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--not-shared-library
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared-library">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared-library">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared-library">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared-library">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared-library">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
--not-shared-moment
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared-moment">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared-moment">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared-moment">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared-moment">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared-moment">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
@ -1982,6 +2013,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-post-command">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--post-command-error
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-post-command-error">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
@ -2250,6 +2288,36 @@
<li><a href="cli.html#cmdoption-osxphotos-query-shared">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--shared-library
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared-library">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-shared-library">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-shared-library">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared-library">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared-library">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
--shared-moment
<ul>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared-moment">osxphotos-add-locations command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-shared-moment">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-shared-moment">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared-moment">osxphotos-repl command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared-moment">osxphotos-sync command line option</a>
</li>
</ul></li>
<li>
@ -2264,6 +2332,13 @@
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-sidecar-drop-ext">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
--sidecar-template
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-sidecar-template">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
@ -3764,10 +3839,10 @@
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_favorite">not_favorite (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hidden">not_hidden (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_in_album">not_in_album (osxphotos.QueryOptions attribute)</a>
@ -3791,6 +3866,10 @@
<li><a href="reference.html#osxphotos.QueryOptions.not_selfie">not_selfie (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_shared">not_shared (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_shared_library">not_shared_library (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_shared_moment">not_shared_moment (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_slow_mo">not_slow_mo (osxphotos.QueryOptions attribute)</a>
</li>
@ -3951,6 +4030,10 @@
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared">--not-shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared-library">--not-shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-shared-moment">--not-shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-not-slow-mo">--not-slow-mo</a>
</li>
@ -3985,6 +4068,10 @@
<li><a href="cli.html#cmdoption-osxphotos-add-locations-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared">--shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared-library">--shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-shared-moment">--shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-add-locations-slow-mo">--slow-mo</a>
</li>
@ -4365,6 +4452,10 @@
<li><a href="cli.html#cmdoption-osxphotos-export-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared">--not-shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared-library">--not-shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-shared-moment">--not-shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-slow-mo">--not-slow-mo</a>
</li>
@ -4393,6 +4484,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-portrait">--portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-post-command">--post-command</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-post-command-error">--post-command-error</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-post-function">--post-function</a>
</li>
@ -4429,10 +4522,16 @@
<li><a href="cli.html#cmdoption-osxphotos-export-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-shared">--shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-shared-library">--shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-shared-moment">--shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-sidecar">--sidecar</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-sidecar-drop-ext">--sidecar-drop-ext</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-sidecar-template">--sidecar-template</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-skip-bursts">--skip-bursts</a>
</li>
@ -4912,6 +5011,10 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared">--not-shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared-library">--not-shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-shared-moment">--not-shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-slow-mo">--not-slow-mo</a>
</li>
@ -4950,6 +5053,10 @@
<li><a href="cli.html#cmdoption-osxphotos-query-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-shared">--shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-shared-library">--shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-shared-moment">--shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-slow-mo">--slow-mo</a>
</li>
@ -5103,6 +5210,10 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared">--not-shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared-library">--not-shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-shared-moment">--not-shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-slow-mo">--not-slow-mo</a>
</li>
@ -5137,6 +5248,10 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared">--shared</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared-library">--shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-shared-moment">--shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-slow-mo">--slow-mo</a>
</li>
@ -5321,6 +5436,10 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-not-screenshot">--not-screenshot</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-selfie">--not-selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared-library">--not-shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-shared-moment">--not-shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-not-slow-mo">--not-slow-mo</a>
</li>
@ -5357,6 +5476,10 @@
<li><a href="cli.html#cmdoption-osxphotos-sync-selfie">--selfie</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-s">--set</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared-library">--shared-library</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-shared-moment">--shared-moment</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-sync-slow-mo">--slow-mo</a>
</li>
@ -5813,15 +5936,33 @@
<li><a href="reference.html#osxphotos.ExportDB.set_photoinfo_for_uuid">set_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
</li>
<li><a href="reference.html#osxphotos.ExifTool.setvalue">setvalue() (osxphotos.ExifTool method)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.share_info">share_info (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.share_participants">share_participants (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoInfo.shared">shared (osxphotos.PhotoInfo property)</a>
<ul>
<li><a href="reference.html#osxphotos.QueryOptions.shared">(osxphotos.QueryOptions attribute)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.PhotoInfo.shared_library">shared_library (osxphotos.PhotoInfo property)</a>
<ul>
<li><a href="reference.html#osxphotos.QueryOptions.shared_library">(osxphotos.QueryOptions attribute)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.PhotoInfo.shared_moment">shared_moment (osxphotos.PhotoInfo property)</a>
<ul>
<li><a href="reference.html#osxphotos.QueryOptions.shared_moment">(osxphotos.QueryOptions attribute)</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.PhotoInfo.shared_moment_info">shared_moment_info (osxphotos.PhotoInfo property)</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.sidecar">sidecar (osxphotos.ExportOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.ExportOptions.sidecar_drop_ext">sidecar_drop_ext (osxphotos.ExportOptions attribute)</a>

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.60.7 documentation</title>
<title>osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -282,6 +283,306 @@
<li class="toctree-l2"><a class="reference internal" href="package_overview.html#using-the-osxphotos-cli-to-run-python-code">Using the osxphotos CLI to run python code</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="API_README.html">OSXPhotos Python API</a><ul>
<li class="toctree-l2"><a class="reference internal" href="API_README.html#table-of-contents">Table of Contents</a></li>
<li class="toctree-l2"><a class="reference internal" href="API_README.html#id1">Example uses of the Python package</a><ul>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#print-filename-date-created-title-and-keywords-for-all-photos-in-a-library">Print filename, date created, title, and keywords for all photos in a library</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#building-simple-command-line-tools">Building simple command line tools</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="API_README.html#concurrency">Concurrency</a></li>
<li class="toctree-l2"><a class="reference internal" href="API_README.html#id6">Package Interface</a><ul>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#a-name-photosdb-photosdb-a"><span class="raw-html-m2r"><a name="photosdb">PhotosDB</a></span></a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#read-a-photos-library-database">Read a Photos library database</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#open-the-default-last-opened-photos-library">Open the default (last opened) Photos library</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#open-system-photos-library">Open System Photos library</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#open-a-specific-photos-library">Open a specific Photos library</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdbphotos-photos-keywords-none-uuid-none-persons-none-albums-none-images-true-movies-true-from-date-none-to-date-none-intrash-false-a"><span class="raw-html-m2r"><A name="photosdbphotos"></span><code class="docutils literal notranslate"><span class="pre">photos(keywords=None,</span> <span class="pre">uuid=None,</span> <span class="pre">persons=None,</span> <span class="pre">albums=None,</span> <span class="pre">images=True,</span> <span class="pre">movies=True,</span> <span class="pre">from_date=None,</span> <span class="pre">to_date=None,</span> <span class="pre">intrash=False)</span></code>&lt;/a&gt;</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-getphoto-get-photo-uuid-a"><span class="raw-html-m2r"><a name="getphoto"></span><code class="docutils literal notranslate"><span class="pre">get_photo(uuid)</span></code>&lt;/A&gt;</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-query-query-options-queryoptions-list-photoinfo-a"><span class="raw-html-m2r"><A name="photosdb_query"></span><code class="docutils literal notranslate"><span class="pre">query(options:</span> <span class="pre">QueryOptions)</span> <span class="pre">-&gt;</span> <span class="pre">List[PhotoInfo]:</span></code>&lt;/a&gt;</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-keywords-keywords-a"><span class="raw-html-m2r"><a name="photosdb_keywords">`keywords`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-albuminfo-album-info-a"><span class="raw-html-m2r"><a name="photosdb_albuminfo">`album_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-albums-albums-a"><span class="raw-html-m2r"><a name="photosdb_albums">`albums`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#albums-shared"><code class="docutils literal notranslate"><span class="pre">albums_shared</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-import-info-import-info-a"><span class="raw-html-m2r"><a name = "photosdb_import_info">`import_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-project-info-project-info-a"><span class="raw-html-m2r"><a name="photosdb_project_info">`project_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-moment-info-moment-info-a"><span class="raw-html-m2r"><a name="photosdb_moment_info">`moment_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-folder-info-folder-info-a"><span class="raw-html-m2r"><a name="photosdb_folder_info">`folder_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-folders-folders-a"><span class="raw-html-m2r"><a name="photosdb_folders">`folders`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-persons-persons-a"><span class="raw-html-m2r"><a name="photosdb_persons">`persons`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-person-info-person-info-a"><span class="raw-html-m2r"><a name="photosdb_person_info">`person_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#keywords-as-dict"><code class="docutils literal notranslate"><span class="pre">keywords_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#persons-as-dict"><code class="docutils literal notranslate"><span class="pre">persons_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#albums-as-dict"><code class="docutils literal notranslate"><span class="pre">albums_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#albums-shared-as-dict"><code class="docutils literal notranslate"><span class="pre">albums_shared_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-labels-labels-a"><span class="raw-html-m2r"><a name="photosdb_labels">`labels`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photosdb-labels-normalized-labels-normalized-a"><span class="raw-html-m2r"><a name="photosdb_labels_normalized">`labels_normalized`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#labels-as-dict"><code class="docutils literal notranslate"><span class="pre">labels_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id19"><code class="docutils literal notranslate"><span class="pre">labels_normalized_as_dict</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#library-path"><code class="docutils literal notranslate"><span class="pre">library_path</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#db-path"><code class="docutils literal notranslate"><span class="pre">db_path</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#db-version"><code class="docutils literal notranslate"><span class="pre">db_version</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#photos-version"><code class="docutils literal notranslate"><span class="pre">photos_version</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#get-db-connection"><code class="docutils literal notranslate"><span class="pre">get_db_connection()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#execute-sql"><code class="docutils literal notranslate"><span class="pre">execute(sql)</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id21">QueryOptions</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#attributes">Attributes</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#a-name-photoinfo-photoinfo-a"><span class="raw-html-m2r"><a name="photoinfo">PhotoInfo</a></span></a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#uuid"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#filename"><code class="docutils literal notranslate"><span class="pre">filename</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id22"><code class="docutils literal notranslate"><span class="pre">original_filename</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#date"><code class="docutils literal notranslate"><span class="pre">date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#date-added"><code class="docutils literal notranslate"><span class="pre">date_added</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#date-modified"><code class="docutils literal notranslate"><span class="pre">date_modified</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#description"><code class="docutils literal notranslate"><span class="pre">description</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#title"><code class="docutils literal notranslate"><span class="pre">title</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#keywords"><code class="docutils literal notranslate"><span class="pre">keywords</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id24"><code class="docutils literal notranslate"><span class="pre">albums</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id26"><code class="docutils literal notranslate"><span class="pre">album_info</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#import-info"><code class="docutils literal notranslate"><span class="pre">import_info</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#project-info"><code class="docutils literal notranslate"><span class="pre">project_info</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#persons"><code class="docutils literal notranslate"><span class="pre">persons</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-personinfo-person-info-a"><span class="raw-html-m2r"><a name="photoinfo_personinfo">`person_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photooinfo-faceinfo-face-info-a"><span class="raw-html-m2r"><a name="photooinfo_faceinfo">`face_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#path"><code class="docutils literal notranslate"><span class="pre">path</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#path-edited"><code class="docutils literal notranslate"><span class="pre">path_edited</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#path-derivatives"><code class="docutils literal notranslate"><span class="pre">path_derivatives</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#path-raw"><code class="docutils literal notranslate"><span class="pre">path_raw</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#has-raw"><code class="docutils literal notranslate"><span class="pre">has_raw</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#israw"><code class="docutils literal notranslate"><span class="pre">israw</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#raw-original"><code class="docutils literal notranslate"><span class="pre">raw_original</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#height"><code class="docutils literal notranslate"><span class="pre">height</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#width"><code class="docutils literal notranslate"><span class="pre">width</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#orientation"><code class="docutils literal notranslate"><span class="pre">orientation</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id38"><code class="docutils literal notranslate"><span class="pre">original_height</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id40"><code class="docutils literal notranslate"><span class="pre">original_width</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id42"><code class="docutils literal notranslate"><span class="pre">original_orientation</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#original-filesize"><code class="docutils literal notranslate"><span class="pre">original_filesize</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id44"><code class="docutils literal notranslate"><span class="pre">ismissing</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id46"><code class="docutils literal notranslate"><span class="pre">hasadjustments</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#adjustments"><code class="docutils literal notranslate"><span class="pre">adjustments</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#external-edit"><code class="docutils literal notranslate"><span class="pre">external_edit</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#favorite"><code class="docutils literal notranslate"><span class="pre">favorite</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id48"><code class="docutils literal notranslate"><span class="pre">hidden</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#visible"><code class="docutils literal notranslate"><span class="pre">visible</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#intrash"><code class="docutils literal notranslate"><span class="pre">intrash</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#date-trashed"><code class="docutils literal notranslate"><span class="pre">date_trashed</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#location"><code class="docutils literal notranslate"><span class="pre">location</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#place"><code class="docutils literal notranslate"><span class="pre">place</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id50"><code class="docutils literal notranslate"><span class="pre">shared</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#owner"><code class="docutils literal notranslate"><span class="pre">owner</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#comments"><code class="docutils literal notranslate"><span class="pre">comments</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#likes"><code class="docutils literal notranslate"><span class="pre">likes</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#isreference"><code class="docutils literal notranslate"><span class="pre">isreference</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#isphoto"><code class="docutils literal notranslate"><span class="pre">isphoto</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#ismovie"><code class="docutils literal notranslate"><span class="pre">ismovie</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#iscloudasset"><code class="docutils literal notranslate"><span class="pre">iscloudasset</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id61"><code class="docutils literal notranslate"><span class="pre">incloud</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#syndicated"><code class="docutils literal notranslate"><span class="pre">syndicated</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#saved-to-library"><code class="docutils literal notranslate"><span class="pre">saved_to_library</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#shared-moment"><code class="docutils literal notranslate"><span class="pre">shared_moment</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#shared-library"><code class="docutils literal notranslate"><span class="pre">shared_library</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#uti"><code class="docutils literal notranslate"><span class="pre">uti</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#uti-original"><code class="docutils literal notranslate"><span class="pre">uti_original</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#uti-edited"><code class="docutils literal notranslate"><span class="pre">uti_edited</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#uti-raw"><code class="docutils literal notranslate"><span class="pre">uti_raw</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id64"><code class="docutils literal notranslate"><span class="pre">burst</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#burst-selected"><code class="docutils literal notranslate"><span class="pre">burst_selected</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#burst-key"><code class="docutils literal notranslate"><span class="pre">burst_key</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#burst-default-pick"><code class="docutils literal notranslate"><span class="pre">burst_default_pick</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id65"><code class="docutils literal notranslate"><span class="pre">burst_photos</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id67"><code class="docutils literal notranslate"><span class="pre">burst_albums</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id70"><code class="docutils literal notranslate"><span class="pre">burst_album_info</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#live-photo"><code class="docutils literal notranslate"><span class="pre">live_photo</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id74"><code class="docutils literal notranslate"><span class="pre">path_live_photo</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#path-edited-live-photo"><code class="docutils literal notranslate"><span class="pre">path_edited_live_photo</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#portrait"><code class="docutils literal notranslate"><span class="pre">portrait</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#hdr"><code class="docutils literal notranslate"><span class="pre">hdr</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#selfie"><code class="docutils literal notranslate"><span class="pre">selfie</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#time-lapse"><code class="docutils literal notranslate"><span class="pre">time_lapse</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#panorama"><code class="docutils literal notranslate"><span class="pre">panorama</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#slow-mo"><code class="docutils literal notranslate"><span class="pre">slow_mo</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id78"><code class="docutils literal notranslate"><span class="pre">labels</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id80"><code class="docutils literal notranslate"><span class="pre">labels_normalized</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-searchinfo-search-info-a"><span class="raw-html-m2r"><a name="photoinfo_searchinfo">`search_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-search-info-normalized-search-info-normalized-a"><span class="raw-html-m2r"><a name="photoinfo_search_info_normalized">`search_info_normalized`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-exif-info-exif-info-a"><span class="raw-html-m2r"><a name="photoinfo_exif_info">`exif_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-exiftool-exiftool-a"><span class="raw-html-m2r"><a name="photoinfo_exiftool">`exiftool`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#score"><code class="docutils literal notranslate"><span class="pre">score</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#duplicates"><code class="docutils literal notranslate"><span class="pre">duplicates</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#cloud-guid"><code class="docutils literal notranslate"><span class="pre">cloud_guid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#cloud-owner-hashed-id"><code class="docutils literal notranslate"><span class="pre">cloud_owner_hashed_id</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#fingerprint"><code class="docutils literal notranslate"><span class="pre">fingerprint</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#hexdigest"><code class="docutils literal notranslate"><span class="pre">hexdigest</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#tables"><code class="docutils literal notranslate"><span class="pre">tables()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#json"><code class="docutils literal notranslate"><span class="pre">json()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#asdict"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#export"><code class="docutils literal notranslate"><span class="pre">export()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-rendertemplate-render-template-template-str-options-none-a"><span class="raw-html-m2r"><a name="rendertemplate">`render_template(template_str, options=None)`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-photoinfo-detected-text-detected-text-confidence-threshold-text-detection-confidence-threshold-a"><span class="raw-html-m2r"><a name="photoinfo_detected_text">`detected_text(confidence_threshold=TEXT_DETECTION_CONFIDENCE_THRESHOLD)`</a></span></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id94">ExifInfo</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id95">AlbumInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id96"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id97"><code class="docutils literal notranslate"><span class="pre">title</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-albumphotos-photos-a"><span class="raw-html-m2r"><a name="albumphotos">`photos`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#creation-date"><code class="docutils literal notranslate"><span class="pre">creation_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#start-date"><code class="docutils literal notranslate"><span class="pre">start_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#end-date"><code class="docutils literal notranslate"><span class="pre">end_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#folder-list"><code class="docutils literal notranslate"><span class="pre">folder_list</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#folder-names"><code class="docutils literal notranslate"><span class="pre">folder_names</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#parent"><code class="docutils literal notranslate"><span class="pre">parent</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id101"><code class="docutils literal notranslate"><span class="pre">owner</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id104"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id105">ImportInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id106"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-importphotos-photos-a"><span class="raw-html-m2r"><a name="importphotos">`photos`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id108"><code class="docutils literal notranslate"><span class="pre">creation_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id109"><code class="docutils literal notranslate"><span class="pre">start_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id110"><code class="docutils literal notranslate"><span class="pre">end_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id111"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id112">ProjectInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id113"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id114"><code class="docutils literal notranslate"><span class="pre">title</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-projectphotos-photos-a"><span class="raw-html-m2r"><a name="projectphotos">`photos`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id116"><code class="docutils literal notranslate"><span class="pre">creation_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id117"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id118">MomentInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#pk"><code class="docutils literal notranslate"><span class="pre">pk</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id119"><code class="docutils literal notranslate"><span class="pre">location</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id120"><code class="docutils literal notranslate"><span class="pre">title</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#subtitle"><code class="docutils literal notranslate"><span class="pre">subtitle</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id121"><code class="docutils literal notranslate"><span class="pre">start_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id122"><code class="docutils literal notranslate"><span class="pre">end_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id123"><code class="docutils literal notranslate"><span class="pre">date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#modification-date"><code class="docutils literal notranslate"><span class="pre">modification_date</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id124"><code class="docutils literal notranslate"><span class="pre">photos</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id125"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id126">FolderInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id127"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id128"><code class="docutils literal notranslate"><span class="pre">title</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-folderinfo-album-info-album-info-a"><span class="raw-html-m2r"><a name="folderinfo_album_info">`album_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-folderinfo-album-info-shared-album-info-shared-a"><span class="raw-html-m2r"><a name="folderinfo_album_info_shared">`album_info_shared`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#subfolders"><code class="docutils literal notranslate"><span class="pre">subfolders</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id132"><code class="docutils literal notranslate"><span class="pre">parent</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#sort-order"><code class="docutils literal notranslate"><span class="pre">sort_order</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#photo-index-photo"><code class="docutils literal notranslate"><span class="pre">photo_index(photo)</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id134"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id135">PlaceInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#ishome"><code class="docutils literal notranslate"><span class="pre">ishome</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#name"><code class="docutils literal notranslate"><span class="pre">name</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#names"><code class="docutils literal notranslate"><span class="pre">names</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#country-code"><code class="docutils literal notranslate"><span class="pre">country_code</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#address-str"><code class="docutils literal notranslate"><span class="pre">address_str</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#address"><code class="docutils literal notranslate"><span class="pre">address</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id136">ScoreInfo</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id137">SearchInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id138"><code class="docutils literal notranslate"><span class="pre">labels</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#place-names"><code class="docutils literal notranslate"><span class="pre">place_names</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#streets"><code class="docutils literal notranslate"><span class="pre">streets</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#neighborhoods"><code class="docutils literal notranslate"><span class="pre">neighborhoods</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#locality-names"><code class="docutils literal notranslate"><span class="pre">locality_names</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#city"><code class="docutils literal notranslate"><span class="pre">city</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#state"><code class="docutils literal notranslate"><span class="pre">state</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#state-abbreviation"><code class="docutils literal notranslate"><span class="pre">state_abbreviation</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#country"><code class="docutils literal notranslate"><span class="pre">country</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#month"><code class="docutils literal notranslate"><span class="pre">month</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#year"><code class="docutils literal notranslate"><span class="pre">year</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#bodies-of-water"><code class="docutils literal notranslate"><span class="pre">bodies_of_water</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#holidays"><code class="docutils literal notranslate"><span class="pre">holidays</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#activities"><code class="docutils literal notranslate"><span class="pre">activities</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#season"><code class="docutils literal notranslate"><span class="pre">season</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#venues"><code class="docutils literal notranslate"><span class="pre">venues</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#venue-types"><code class="docutils literal notranslate"><span class="pre">venue_types</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#media-types"><code class="docutils literal notranslate"><span class="pre">media_types</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#all"><code class="docutils literal notranslate"><span class="pre">all</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id139"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id140">PersonInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id141"><code class="docutils literal notranslate"><span class="pre">name</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#display-name"><code class="docutils literal notranslate"><span class="pre">display_name</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id142"><code class="docutils literal notranslate"><span class="pre">uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#keyphoto"><code class="docutils literal notranslate"><span class="pre">keyphoto</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#facecount"><code class="docutils literal notranslate"><span class="pre">facecount</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-personphotos-photos-a"><span class="raw-html-m2r"><a name="personphotos">`photos`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-personfaceinfo-face-info-a"><span class="raw-html-m2r"><a name="personfaceinfo">`face_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-personfavorite-favorite-a"><span class="raw-html-m2r"><a name="personfavorite">`favorite`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-personsortorder-sort-order-a"><span class="raw-html-m2r"><a name="personsortorder">`sort_order`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id144"><code class="docutils literal notranslate"><span class="pre">json()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id145"><code class="docutils literal notranslate"><span class="pre">asdict()</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id146">FaceInfo</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-uuid-uuid-a"><span class="raw-html-m2r"><a name="faceinfo_uuid">`uuid`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-name-name-a"><span class="raw-html-m2r"><a name="faceinfo_name">`name`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#asset-uuid"><code class="docutils literal notranslate"><span class="pre">asset_uuid</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-person-info-person-info-a"><span class="raw-html-m2r"><a name="faceinfo_person_info">`person_info`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-photo-photo-a"><span class="raw-html-m2r"><a name="faceinfo_photo">`photo`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#mwg-rs-area"><code class="docutils literal notranslate"><span class="pre">mwg_rs_area</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#mpri-reg-rect"><code class="docutils literal notranslate"><span class="pre">mpri_reg_rect</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#face-rect"><code class="docutils literal notranslate"><span class="pre">face_rect()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#center"><code class="docutils literal notranslate"><span class="pre">center</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#size-pixels"><code class="docutils literal notranslate"><span class="pre">size_pixels</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#roll-pitch-yaw"><code class="docutils literal notranslate"><span class="pre">roll_pitch_yaw()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#roll">roll</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#pitch">pitch</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#yaw">yaw</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#additional-properties"><code class="docutils literal notranslate"><span class="pre">Additional</span> <span class="pre">properties</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-asdict-asdict-a"><span class="raw-html-m2r"><a name="faceinfo_asdict">`asdict()`</a></span></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#a-name-faceinfo-json-json-a"><span class="raw-html-m2r"><a name="faceinfo_json">`json()`</a></span></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id149">CommentInfo</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id150">LikeInfo</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id151">AdjustmentsInfo</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id152">PhotoTables</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id153">Raw Photos</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#raw-related-attributes">Raw-Related Attributes</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#example">Example</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id162">Template System</a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#a-name-exiftoolexiftool-exiftool-a"><span class="raw-html-m2r"><a name="exiftoolExifTool">ExifTool</a></span></a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#exiftool-methods">ExifTool methods</a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#implementation-note">Implementation Note</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#a-name-photoexporter-photoexporter-a"><span class="raw-html-m2r"><a name="photoexporter">PhotoExporter</a></span></a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#export-dest-filename-none-options-optional-exportoptions-none-exportresults"><code class="docutils literal notranslate"><span class="pre">export(dest,</span> <span class="pre">filename=None,</span> <span class="pre">options:</span> <span class="pre">Optional[ExportOptions]=None)</span> <span class="pre">-&gt;</span> <span class="pre">ExportResults</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#exportoptions"><code class="docutils literal notranslate"><span class="pre">ExportOptions</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#exportresults"><code class="docutils literal notranslate"><span class="pre">ExportResults</span></code></a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#a-name-textdetection-text-detection-a"><span class="raw-html-m2r"><a name="textdetection">Text Detection</a></span></a></li>
<li class="toctree-l3"><a class="reference internal" href="API_README.html#id165">Utility Functions</a><ul>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id166"><code class="docutils literal notranslate"><span class="pre">get_system_library_path()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#id167"><code class="docutils literal notranslate"><span class="pre">get_last_library_path()</span></code></a></li>
<li class="toctree-l4"><a class="reference internal" href="API_README.html#list-photo-libraries"><code class="docutils literal notranslate"><span class="pre">list_photo_libraries()</span></code></a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="API_README.html#id168">Additional Examples</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a><ul>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo"><code class="docutils literal notranslate"><span class="pre">AlbumInfo</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.AlbumInfo.asdict"><code class="docutils literal notranslate"><span class="pre">AlbumInfo.asdict()</span></code></a></li>
@ -541,7 +842,12 @@
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.search_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.search_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.search_info_normalized"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.search_info_normalized</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.selfie"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.selfie</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.share_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.share_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.share_participants"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.share_participants</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.shared"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.shared_library"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared_library</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.shared_moment"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared_moment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.shared_moment_info"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.shared_moment_info</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.slow_mo"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.slow_mo</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.syndicated"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.syndicated</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.PhotoInfo.tables"><code class="docutils literal notranslate"><span class="pre">PhotoInfo.tables()</span></code></a></li>
@ -710,6 +1016,10 @@
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_syndicated"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_syndicated</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.saved_to_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.saved_to_library</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_saved_to_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_saved_to_library</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.shared_moment"><code class="docutils literal notranslate"><span class="pre">QueryOptions.shared_moment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_shared_moment"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_shared_moment</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.shared_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.shared_library</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="reference.html#osxphotos.QueryOptions.not_shared_library"><code class="docutils literal notranslate"><span class="pre">QueryOptions.not_shared_library</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="reference.html#osxphotos.ScoreInfo"><code class="docutils literal notranslate"><span class="pre">ScoreInfo</span></code></a><ul>

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.60.7 documentation</title>
<title>OSXPhotos - osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>

View File

@ -3,10 +3,10 @@
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
<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" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python API" href="API_README.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.60.7 documentation</title>
<title>OSXPhotos Python Package Overview - osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,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 current current-page"><a class="current reference internal" href="#">OSXPhotos Python Package Overview</a></li>
<li class="toctree-l1"><a class="reference internal" href="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -387,12 +388,12 @@ as well as <code class="docutils literal notranslate"><span class="pre">{functio
<footer>
<div class="related-pages">
<a class="next-page" href="reference.html">
<a class="next-page" href="API_README.html">
<div class="page-info">
<div class="context">
<span>Next</span>
</div>
<div class="title">OSXPhotos Python Reference</div>
<div class="title">OSXPhotos Python API</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>

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.60.7 documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.62.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" />
@ -122,7 +122,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -159,6 +159,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>

File diff suppressed because one or more lines are too long

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.60.7 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.62.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" />
<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.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.1 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
@ -158,6 +158,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>

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.60.7 documentation</title>
<title>OSXPhotos Template System - osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,7 @@
<li class="toctree-l1"><a class="reference internal" href="cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>
@ -320,8 +321,8 @@
<p>If you want to include “{” or “}” in the output, use “{openbrace}” or “{closebrace}” template substitution.</p>
<p>e.g. <code class="docutils literal notranslate"><span class="pre">&quot;{created.year}/{openbrace}{title}{closebrace}&quot;</span></code> would result in <code class="docutils literal notranslate"><span class="pre">&quot;2020/{Photo</span> <span class="pre">Title}&quot;</span></code>.</p>
<p><strong>Variables</strong></p>
<p>You can define variables for later use in the template string using the format <code class="docutils literal notranslate"><span class="pre">{var:NAME,VALUE}</span></code>. Variables may then be referenced using the format <code class="docutils literal notranslate"><span class="pre">%NAME</span></code>. For example: <code class="docutils literal notranslate"><span class="pre">{var:foo,bar}</span></code> defines the variable <code class="docutils literal notranslate"><span class="pre">%foo</span></code> to have value <code class="docutils literal notranslate"><span class="pre">bar</span></code>. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the “pipe” (<code class="docutils literal notranslate"><span class="pre">|</span></code>) character is not allowed in a find/replace pair but you can get around this limitation like so: <code class="docutils literal notranslate"><span class="pre">{var:pipe,{pipe}}{title[-,%pipe]}</span></code> which replaces the <code class="docutils literal notranslate"><span class="pre">-</span></code> character with <code class="docutils literal notranslate"><span class="pre">|</span></code> (the value of <code class="docutils literal notranslate"><span class="pre">%pipe</span></code>).</p>
<p>Variables can also be referenced as fields in the template string, for example: <code class="docutils literal notranslate"><span class="pre">{var:year,created.year}{original_name}-{%year}</span></code>. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: <code class="docutils literal notranslate"><span class="pre">{var:name,Katie}{person</span> <span class="pre">contains</span> <span class="pre">{%name}?{%name},Not-{%name}}</span></code>.</p>
<p>You can define variables for later use in the template string using the format <code class="docutils literal notranslate"><span class="pre">{var:NAME,VALUE}</span></code> where <code class="docutils literal notranslate"><span class="pre">VALUE</span></code> is a template statement. Variables may then be referenced using the format <code class="docutils literal notranslate"><span class="pre">%NAME</span></code>. For example: <code class="docutils literal notranslate"><span class="pre">{var:foo,bar}</span></code> defines the variable <code class="docutils literal notranslate"><span class="pre">%foo</span></code> to have value <code class="docutils literal notranslate"><span class="pre">bar</span></code>. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the “pipe” (<code class="docutils literal notranslate"><span class="pre">|</span></code>) character is not allowed in a find/replace pair but you can get around this limitation like so: <code class="docutils literal notranslate"><span class="pre">{var:pipe,{pipe}}{title[-,%pipe]}</span></code> which replaces the <code class="docutils literal notranslate"><span class="pre">-</span></code> character with <code class="docutils literal notranslate"><span class="pre">|</span></code> (the value of <code class="docutils literal notranslate"><span class="pre">%pipe</span></code>).</p>
<p>Variables can also be referenced as fields in the template string, for example: <code class="docutils literal notranslate"><span class="pre">{var:year,{created.year}}{original_name}-{%year}</span></code>. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: <code class="docutils literal notranslate"><span class="pre">{var:name,Katie}{person</span> <span class="pre">contains</span> <span class="pre">{%name}?{%name},Not-{%name}}</span></code>.</p>
<p>If you need to use a <code class="docutils literal notranslate"><span class="pre">%</span></code> (percent sign character), you can escape the percent sign by using <code class="docutils literal notranslate"><span class="pre">%%</span></code>. You can also use the <code class="docutils literal notranslate"><span class="pre">{percent}</span></code> template field where a template field is required. For example:</p>
<p><code class="docutils literal notranslate"><span class="pre">{title[:,%%]}</span></code> replaces the <code class="docutils literal notranslate"><span class="pre">:</span></code> with <code class="docutils literal notranslate"><span class="pre">%</span></code> and <code class="docutils literal notranslate"><span class="pre">{title</span> <span class="pre">contains</span> <span class="pre">Foo?{title}{percent},{title}}</span></code> adds <code class="docutils literal notranslate"><span class="pre">%</span></code> to the title if it contains <code class="docutils literal notranslate"><span class="pre">Foo</span></code>.</p>
<section id="id1">
@ -613,7 +614,7 @@
</td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.60.7</p></td>
<td><p>The osxphotos version, e.g. 0.62.1</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.60.7 documentation</title>
<title>OSXPhotos Tutorial - osxphotos 0.62.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" />
@ -124,7 +124,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">osxphotos 0.60.7 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.62.1 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.60.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.62.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">
@ -161,6 +161,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="API_README.html">OSXPhotos Python API</a></li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">OSXPhotos Python Reference</a></li>
</ul>

3287
docsrc/source/api_readme.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ Welcome to OSXPhotos's documentation!
cli
template_help
package_overview
API_README
reference

View File

@ -162,9 +162,9 @@ e.g. ``"{created.year}/{openbrace}{title}{closebrace}"`` would result in ``"2020
**Variables**
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}``. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
You can define variables for later use in the template string using the format ``{var:NAME,VALUE}`` where ``VALUE`` is a template statement. Variables may then be referenced using the format ``%NAME``. For example: ``{var:foo,bar}`` defines the variable ``%foo`` to have value ``bar``. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (\ ``|``\ ) character is not allowed in a find/replace pair but you can get around this limitation like so: ``{var:pipe,{pipe}}{title[-,%pipe]}`` which replaces the ``-`` character with ``|`` (the value of ``%pipe``\ ).
Variables can also be referenced as fields in the template string, for example: ``{var:year,created.year}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
Variables can also be referenced as fields in the template string, for example: ``{var:year,{created.year}}{original_name}-{%year}``. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: ``{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}``.
If you need to use a ``%`` (percent sign character), you can escape the percent sign by using ``%%``. You can also use the ``{percent}`` template field where a template field is required. For example:
@ -361,7 +361,7 @@ Template Substitutions
* - {tab}
- :A tab: '\t'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.60.7'
- The osxphotos version, e.g. '0.62.1'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

@ -0,0 +1,107 @@
"""Add the photo size in form widthxheight to each photo's caption/description in Photos
Run this with `osxphotos run add_size_to_caption.py`
Run `osxphotos run add_size_to_caption.py --help` for help.
Intended to be run with osxphotos; see https://github.com/RhetTbull/osxphotos
This may be helpful for allowing Smart Albums in Photos to filter by photo size.
Reference this reddit post: https://www.reddit.com/r/ApplePhotos/comments/15r4fk6/smart_album_filter_for_photovideo_dimensions/
"""
from __future__ import annotations
import click
import photoscript
import osxphotos
from osxphotos.cli import echo, echo_error, query_command, verbose
from osxphotos.utils import pluralize
@query_command
@click.option(
"--original",
is_flag=True,
help="If photo is edited, use original image size instead of edited image size",
)
@click.option(
"--clear",
is_flag=True,
help="Remove existing size information from description",
)
def main(photos: list[osxphotos.PhotoInfo], original: bool, clear: bool, **kwargs):
"""Add the photo size in form widthxheight to each photo's caption in Photos
If a photo has a caption, the size will be appended to the caption.
Use --original to use the original image size instead of the edited image size.
Use --clear to remove existing size information from caption.
"""
# filter out any shared photos
photos = [p for p in photos if not p.shared]
echo(f"Processing {len(photos)} {pluralize(len(photos), 'photo', 'photos')}...")
if clear:
clear_size_str_from_photos(photos, original)
else:
add_size_str_to_photos(photos, original)
echo("Done.")
def compute_size_str(photo: osxphotos.PhotoInfo, original: bool) -> str:
"""Return the size string for a given photo"""
return (
f"{photo.original_width}x{photo.original_height}"
if original
else f"{photo.width}x{photo.height}"
)
def add_size_str_to_photos(photos: list[osxphotos.PhotoInfo], original: bool):
"""Add size string to photo description/caption"""
for photo in photos:
size_str = compute_size_str(photo, original)
description = photo.description or "" # description can be None
if size_str in description:
verbose(
f"Skipping {photo.original_filename} ({photo.uuid}) ({size_str} already in caption)"
)
continue
new_desc = f"{photo.description}\n{size_str}" if description else size_str
verbose(
f"Updating caption for {photo.original_filename} ({photo.uuid}) to {new_desc}"
)
update_description(photo, new_desc)
def clear_size_str_from_photos(photos: list[osxphotos.PhotoInfo], original: bool):
"""Clear size string from photo description/caption"""
for photo in photos:
size_str = compute_size_str(photo, original)
description = photo.description or "" # description can be None
if size_str not in description:
verbose(
f"Skipping {photo.original_filename} ({photo.uuid}) ({size_str} not in caption)"
)
continue
new_desc = description.replace(size_str, "").strip()
verbose(
f"Setting caption for {photo.original_filename} ({photo.uuid}) to {new_desc}"
)
update_description(photo, new_desc)
def update_description(photo: osxphotos.PhotoInfo, new_desc: str):
"""Update photo caption"""
try:
photoscript.Photo(photo.uuid).description = new_desc
except Exception as e:
echo_error(
f"Error updating caption for {photo.original_filename} ({photo.uuid}): {e}"
)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,22 @@
<%doc>
This is an example Mako template for use with --sidecar-template
For more information on Mako templates, see https://docs.makotemplates.org/en/latest/
The template will be passed three variables for rendering:
photo: a PhotoInfo object for the photo being exported
photo_path: a pathlib.Path object for the photo file being exported
sidecar_path: a pathlib.Path object for the sidecar file being written
</%doc>
<%def name="rating(photo)" filter="trim">\
% if photo.favorite:
★★★★★
% else:
★☆☆☆☆
% endif
</%def>\
Photo: ${photo_path.name}
UUID: ${photo.uuid}
Sidecar: ${sidecar_path.name}
Rating: ${rating(photo)}

View File

@ -0,0 +1,11 @@
<%doc>
Mako template to dump a full json representation of the photo object
Can be run from the command line with:
osxphotos export /path/to/export --sidecar-template custom_sidecar_json.mako "{filepath}.json" yes no yes
The template will be passed three variables for rendering:
photo: a PhotoInfo object for the photo being exported
photo_path: a pathlib.Path object for the photo file being exported
sidecar_path: a pathlib.Path object for the sidecar file being written
</%doc>
${photo.json(shallow=False, indent=4)}

View File

@ -0,0 +1,223 @@
<%doc>
This is an example Mako template for use with --sidecar-template which produces an XMP sidecar file
For more information on Mako templates, see https://docs.makotemplates.org/en/latest/
The template will be passed three variables for rendering:
photo: a PhotoInfo object for the photo being exported
photo_path: a pathlib.Path object for the photo file being exported
sidecar_path: a pathlib.Path object for the sidecar file being written
</%doc>
<%def name="photoshop_sidecar_for_extension(extension)">
% if extension is None:
<photoshop:SidecarForExtension></photoshop:SidecarForExtension>
% else:
<photoshop:SidecarForExtension>${extension}</photoshop:SidecarForExtension>
% endif
</%def>
<%def name="dc_description(desc)">
% if desc is None:
<dc:description>
<rdf:Alt>
<rdf:li xml:lang='x-default'/>
</rdf:Alt>
</dc:description>
% else:
<dc:description>
<rdf:Alt>
<rdf:li xml:lang='x-default'>${desc | x}</rdf:li>
</rdf:Alt>
</dc:description>
% endif
</%def>
<%def name="dc_title(title)">
% if title is None:
<dc:title>
<rdf:Alt>
<rdf:li xml:lang='x-default'/>
</rdf:Alt>
</dc:title>
% else:
<dc:title>
<rdf:Alt>
<rdf:li xml:lang='x-default'>${title | x}</rdf:li>
</rdf:Alt>
</dc:title>
% endif
</%def>
<%def name="dc_subject(subject)">
% if subject:
<dc:subject>
<rdf:Bag>
% for subj in subject:
<rdf:li>${subj | x}</rdf:li>
% endfor
</rdf:Bag>
</dc:subject>
% endif
</%def>
<%def name="dc_datecreated(date)">
% if date is not None:
<photoshop:DateCreated>${date.isoformat()}</photoshop:DateCreated>
% endif
</%def>
<%def name="iptc_personinimage(persons)">
% if persons:
<Iptc4xmpExt:PersonInImage>
<rdf:Bag>
% for person in persons:
<rdf:li>${person | x}</rdf:li>
% endfor
</rdf:Bag>
</Iptc4xmpExt:PersonInImage>
% endif
</%def>
<%def name="dk_tagslist(keywords)">
% if keywords:
<digiKam:TagsList>
<rdf:Seq>
% for keyword in keywords:
<rdf:li>${keyword | x}</rdf:li>
% endfor
</rdf:Seq>
</digiKam:TagsList>
% endif
</%def>
<%def name="adobe_createdate(date)">
% if date is not None:
<xmp:CreateDate>${date.strftime("%Y-%m-%dT%H:%M:%S")}</xmp:CreateDate>
% endif
</%def>
<%def name="adobe_modifydate(date)">
% if date is not None:
<xmp:ModifyDate>${date.strftime("%Y-%m-%dT%H:%M:%S")}</xmp:ModifyDate>
% endif
</%def>
<%def name="xmp_rating(rating)">
% if rating is not None:
<xmp:Rating>${rating}</xmp:Rating>
% endif
</%def>
<%def name="gps_info(latitude, longitude)">
% if latitude is not None and longitude is not None:
<exif:GPSLongitude>${int(abs(longitude))},${(abs(longitude) % 1) * 60}${"E" if longitude >= 0 else "W"}</exif:GPSLongitude>
<exif:GPSLatitude>${int(abs(latitude))},${(abs(latitude) % 1) * 60}${"N" if latitude >= 0 else "S"}</exif:GPSLatitude>
% endif
</%def>
<%def name="mwg_face_regions(photo)">
% if photo.face_info:
<mwg-rs:Regions rdf:parseType="Resource">
<mwg-rs:AppliedToDimensions rdf:parseType="Resource">
<stDim:h>${photo.width if photo.orientation in [5, 6, 7, 8] else photo.height}</stDim:h>
<stDim:w>${photo.height if photo.orientation in [5, 6, 7, 8] else photo.width}</stDim:w>
<stDim:unit>pixel</stDim:unit>
</mwg-rs:AppliedToDimensions>
<mwg-rs:RegionList>
<rdf:Bag>
% for face in photo.face_info:
<rdf:li rdf:parseType="Resource">
<mwg-rs:Area rdf:parseType="Resource">
<stArea:h>${'{0:.6f}'.format(face.mwg_rs_area.h)}</stArea:h>
<stArea:w>${'{0:.6f}'.format(face.mwg_rs_area.w)}</stArea:w>
<stArea:x>${'{0:.6f}'.format(face.mwg_rs_area.x)}</stArea:x>
<stArea:y>${'{0:.6f}'.format(face.mwg_rs_area.y)}</stArea:y>
<stArea:unit>normalized</stArea:unit>
</mwg-rs:Area>
<mwg-rs:Name>${face.name}</mwg-rs:Name>
<mwg-rs:Rotation>${face.roll}</mwg-rs:Rotation>
<mwg-rs:Type>Face</mwg-rs:Type>
</rdf:li>
% endfor
</rdf:Bag>
</mwg-rs:RegionList>
</mwg-rs:Regions>
% endif
</%def>
<%def name="mpri_face_regions(photo)">
% if photo.face_info:
<MP:RegionInfo rdf:parseType="Resource">
<MPRI:Regions>
<rdf:Bag>
% for face in photo.face_info:
<rdf:li rdf:parseType="Resource">
<MPReg:PersonDisplayName>${face.name}</MPReg:PersonDisplayName>
<MPReg:Rectangle>${'{0:.6f}'.format(face.mpri_reg_rect.x)}, ${'{0:.6f}'.format(face.mpri_reg_rect.y)}, ${'{0:.6f}'.format(face.mpri_reg_rect.h)}, ${'{0:.6f}'.format(face.mpri_reg_rect.w)}</MPReg:Rectangle>
</rdf:li>
% endfor
</rdf:Bag>
</MPRI:Regions>
</MP:RegionInfo>
% endif
</%def>
<?xpacket begin="${"\uFEFF"}" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="osxphotos">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
<%
extension = photo_path.suffix[1:].lower() if photo_path.suffix else ""
%>
${photoshop_sidecar_for_extension(extension)}
${dc_description(photo.description)}
${dc_title(photo.title)}
<%
subjects = photo.keywords + photo.persons
%>
${dc_subject(subjects)}
${dc_datecreated(photo.date)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:Iptc4xmpExt='http://iptc.org/std/Iptc4xmpExt/2008-02-29/'>
${iptc_personinimage(photo.persons)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:digiKam='http://www.digikam.org/ns/1.0/'>
${dk_tagslist(photo.keywords)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
${adobe_createdate(photo.date)}
${adobe_modifydate(photo.date)}
${xmp_rating("5" if photo.favorite else None)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
${gps_info(*photo.location)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#">
${mwg_face_regions(photo)}
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:MP="http://ns.microsoft.com/photo/1.2/"
xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#"
xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">
${mpri_face_regions(photo)}
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>

View File

@ -1,57 +1,88 @@
""" Example function for use with osxphotos export --post-function option """
from typing import Callable
from __future__ import annotations
import pathlib
from typing import Any, Callable
from osxphotos import ExportResults, PhotoInfo
from osxphotos.cli import echo_error
def post_function(
photo: PhotoInfo, results: ExportResults, verbose: Callable, **kwargs
):
photo: PhotoInfo, results: ExportResults, verbose: Callable[[Any], None], **kwargs
) -> ExportResults | None:
"""Call this with osxphotos export /path/to/export --post-function post_function.py::post_function
This will get called immediately after the photo has been exported
Args:
photo: PhotoInfo instance for the photo that's just been exported
see https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo for details
results: ExportResults instance with information about the files associated with the exported photo
see https://rhettbull.github.io/osxphotos/reference.html#osxphotos.ExportResults for details
verbose: A function to print verbose output if --verbose is set; if --verbose is not set, acts as a no-op (nothing gets printed)
**kwargs: reserved for future use; recommend you include **kwargs so your function still works if additional arguments are added in future versions
Returns:
ExportResults instance or None
If returning an ExportResults instance, it must be a new instance; do not modify the instance passed in as an argument.
You should set only the following values in ExportResults:
user_written: list[str], list of files written by your function
user_skipped: list[str], list of files skipped by your function
user_error: list[tuple[str, str]], list of tuples of (filename, error) for any errors generated by your function
For full description of ExportResults see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.ExportResults
Notes:
Use verbose(str) instead of print if you want your function to conditionally output text depending on --verbose flag
Any string printed with verbose that contains "warning" or "error" (case-insensitive) will be printed with the appropriate warning or error color
Will not be called if --dry-run flag is enabled
Will be called immediately after export and before any --post-command commands are executed
The function will not be called if --dry-run flag is enabled
The function be called immediately after export and before any --post-command commands are executed
If your function does not write any files, you can optionally return None instead of an ExportResults instance
If you return ExportResults, any files in user_written or user_skipped will be included when
writing reports and will be preserved when using --cleanup
If you want files previously written by the post_function, but not written this time,
to be preserved when using --cleanup, you should include them in user_skipped
If the function raises an exception, osxphotos will abort the export and exit with an error.
If you want the export to continue, you should catch any exceptions and return an ExportResults instance
with the error(s) in specified in ExportResults.user_error which is a list of tuples of (filename, error)
The verbose function can be used to print text to stdout if --verbose is set
If --verbose is not set, verbose acts as a no-op (nothing gets printed)
Verbose output may be stylized with tags as follows; tags must be enclosed in square brackets
and closed with [/] to end the style:
[change]: something change
[no_change]: indicate no change
[count]: a count
[error]: an error
[filename]: a filename
[filepath]: a filepath
[num]: a number
[time]: a time or date
[tz]: a timezone
[warning]: a warning
[uuid]: a uuid
"""
# ExportResults has the following properties
# fields with filenames contain the full path to the file
# exported: list of all files exported
# new: list of all new files exported (--update)
# updated: list of all files updated (--update)
# skipped: list of all files skipped (--update)
# exif_updated: list of all files that were updated with --exiftool
# touched: list of all files that had date updated with --touch-file
# converted_to_jpeg: list of files converted to jpeg with --convert-to-jpeg
# sidecar_json_written: list of all JSON sidecar files written
# sidecar_json_skipped: list of all JSON sidecar files skipped (--update)
# sidecar_exiftool_written: list of all exiftool sidecar files written
# sidecar_exiftool_skipped: list of all exiftool sidecar files skipped (--update)
# sidecar_xmp_written: list of all XMP sidecar files written
# sidecar_xmp_skipped: list of all XMP sidecar files skipped (--update)
# missing: list of all missing files
# error: list tuples of (filename, error) for any errors generated during export
# exiftool_warning: list of tuples of (filename, warning) for any warnings generated by exiftool with --exiftool
# exiftool_error: list of tuples of (filename, error) for any errors generated by exiftool with --exiftool
# xattr_written: list of files that had extended attributes written
# xattr_skipped: list of files that where extended attributes were skipped (--update)
# deleted_files: list of deleted files
# deleted_directories: list of deleted directories
# exported_album: list of tuples of (filename, album_name) for exported files added to album with --add-exported-to-album
# skipped_album: list of tuples of (filename, album_name) for skipped files added to album with --add-skipped-to-album
# missing_album: list of tuples of (filename, album_name) for missing files added to album with --add-missing-to-album
# metadata_changed: list of filenames that had metadata changes since last export
for filename in results.exported:
post_results = ExportResults()
for filename in results.exported + results.skipped:
# do your processing here
verbose(f"post_function: {photo.original_filename} exported as {filename}")
# simulate doing some processing
new_filename = pathlib.Path(f"{filename}.new")
if new_filename.exists():
verbose(f"Skipping file [filepath]{new_filename}[/] as it already exists")
post_results.user_skipped.append(new_filename)
else:
verbose(f"Writing new file [filepath]{new_filename}[/]")
new_filename.touch()
post_results.user_written.append(new_filename)
# if you encounter an error, add it to user_error
# echo_error will print the error to stderr with the appropriate formatting
# echo_error(f"Encountered an error processing [filepath]{filename}[/]: [error]some error[/]")
# post_results.user_error.append((filename, "some error"))
# if your function does not write any files, you can return None
return post_results

View File

@ -23,15 +23,18 @@ TIME_DELTA = (datetime(2001, 1, 1, 0, 0) - datetime(1970, 1, 1, 0, 0)).total_sec
# Photos 3.0 (10.13.6) == 3301
# Photos 4.0 (10.14.5) == 4016
# Photos 4.0 (10.14.6) == 4025
# Photos 5.0 (10.15.0) == 6000 or 5001
# Photos 5.0+ (10.15.0) == 6000 or 5001
_TESTED_DB_VERSIONS = ["6000", "5001", "4025", "4016", "3301", "2622"]
# database model versions (applies to Photos 5, Photos 6)
# database model versions (applies to Photos 5+)
# these come from PLModelVersion key in binary plist in Z_METADATA.Z_PLIST
# Photos 5 (10.15.1) == 13537
# Photos 5 (10.15.4, 10.15.5, 10.15.6) == 13703
# Photos 6 (10.16.0 Beta) == 14104
_TEST_MODEL_VERSIONS = ["13537", "13703", "14104"]
# Photos 7 (12.0.1) == 15323
# Photos 8 (13.0.0) == 16320
# Photos 9 (14.0.0 dev preview) = 17120
_TEST_MODEL_VERSIONS = ["13537", "13703", "14104", "15323", "16320", "17120"]
_PHOTOS_2_VERSION = "2622"
@ -39,7 +42,7 @@ _PHOTOS_2_VERSION = "2622"
_PHOTOS_3_VERSION = "3301"
# versions 5.0 and later have a different database structure
_PHOTOS_4_VERSION = "4025" # latest Mojove version on 10.14.6
_PHOTOS_4_VERSION = "4025" # latest Mojave version on 10.14.6
_PHOTOS_5_VERSION = "5000" # I've seen both 5001 and 6000. 6000 is most common on Catalina and up but there are some version 5001 database in the wild
# Ranges for model version by Photos version
@ -47,8 +50,15 @@ _PHOTOS_5_MODEL_VERSION = [13000, 13999]
_PHOTOS_6_MODEL_VERSION = [14000, 14999]
_PHOTOS_7_MODEL_VERSION = [15000, 15999] # Dev preview: 15134, 12.1: 15331
_PHOTOS_8_MODEL_VERSION = [16000, 16999] # Ventura dev preview: 16119
_PHOTOS_9_MODEL_VERSION = [17000, 17999] # Sonoma dev preview: 17120
# some table names differ between Photos 5 and Photos 6
# the preview versions of 12.0.0 had a difference schema for syndication info so need to check model version before processing
_PHOTOS_SYNDICATION_MODEL_VERSION = 15323 # 12.0.1
# shared iCloud library versions; dev preview doesn't contain same columns as release version
_PHOTOS_SHARED_LIBRARY_VERSION = 16320 # 13.0
# some table names differ between Photos 5 and later versions
_DB_TABLE_NAMES = {
5: {
"ASSET": "ZGENERICASSET",
@ -61,6 +71,8 @@ _DB_TABLE_NAMES = {
"ASSET_ALBUM_JOIN": "Z_26ASSETS.Z_26ALBUMS",
"ASSET_ALBUM_TABLE": "Z_26ASSETS",
"HDR_TYPE": "ZCUSTOMRENDEREDVALUE",
"DETECTED_FACE_PERSON_FK": "ZDETECTEDFACE.ZPERSON",
"DETECTED_FACE_ASSET_FK": "ZDETECTEDFACE.ZASSET",
},
6: {
"ASSET": "ZASSET",
@ -73,6 +85,8 @@ _DB_TABLE_NAMES = {
"ASSET_ALBUM_JOIN": "Z_26ASSETS.Z_26ALBUMS",
"ASSET_ALBUM_TABLE": "Z_26ASSETS",
"HDR_TYPE": "ZCUSTOMRENDEREDVALUE",
"DETECTED_FACE_PERSON_FK": "ZDETECTEDFACE.ZPERSON",
"DETECTED_FACE_ASSET_FK": "ZDETECTEDFACE.ZASSET",
},
7: {
"ASSET": "ZASSET",
@ -85,6 +99,8 @@ _DB_TABLE_NAMES = {
"ASSET_ALBUM_JOIN": "Z_27ASSETS.Z_27ALBUMS",
"ASSET_ALBUM_TABLE": "Z_27ASSETS",
"HDR_TYPE": "ZHDRTYPE",
"DETECTED_FACE_PERSON_FK": "ZDETECTEDFACE.ZPERSON",
"DETECTED_FACE_ASSET_FK": "ZDETECTEDFACE.ZASSET",
},
8: {
"ASSET": "ZASSET",
@ -97,6 +113,22 @@ _DB_TABLE_NAMES = {
"ASSET_ALBUM_JOIN": "Z_28ASSETS.Z_28ALBUMS",
"ASSET_ALBUM_TABLE": "Z_28ASSETS",
"HDR_TYPE": "ZHDRTYPE",
"DETECTED_FACE_PERSON_FK": "ZDETECTEDFACE.ZPERSON",
"DETECTED_FACE_ASSET_FK": "ZDETECTEDFACE.ZASSET",
},
9: {
"ASSET": "ZASSET",
"KEYWORD_JOIN": "Z_1KEYWORDS.Z_40KEYWORDS",
"ALBUM_JOIN": "Z_28ASSETS.Z_3ASSETS",
"ALBUM_SORT_ORDER": "Z_28ASSETS.Z_FOK_3ASSETS",
"IMPORT_FOK": "null",
"DEPTH_STATE": "ZASSET.ZDEPTHTYPE",
"UTI_ORIGINAL": "ZINTERNALRESOURCE.ZCOMPACTUTI",
"ASSET_ALBUM_JOIN": "Z_28ASSETS.Z_28ALBUMS",
"ASSET_ALBUM_TABLE": "Z_28ASSETS",
"HDR_TYPE": "ZHDRTYPE",
"DETECTED_FACE_PERSON_FK": "ZDETECTEDFACE.ZPERSONFORFACE",
"DETECTED_FACE_ASSET_FK": "ZDETECTEDFACE.ZASSETFORFACE",
},
}
@ -127,6 +159,7 @@ _TESTED_OS_VERSIONS = [
("13", "2"),
("13", "3"),
("13", "4"),
("14", "0"),
]
# Photos 5 has persons who are empty string if unidentified face

View File

@ -1,3 +1,3 @@
""" version info """
__version__ = "0.60.7"
__version__ = "0.62.1"

View File

@ -611,6 +611,26 @@ _QUERY_PARAMETERS_DICT = {
is_flag=True,
help="Search for syndicated photos that have not saved to the library",
),
"--shared-moment": click.Option(
["--shared-moment"],
is_flag=True,
help="Search for photos that are part of a shared moment",
),
"--not-shared-moment": click.Option(
["--not-shared-moment"],
is_flag=True,
help="Search for photos that are not part of a shared moment",
),
"--shared-library": click.Option(
["--shared-library"],
is_flag=True,
help="Search for photos that are part of a shared library",
),
"--not-shared-library": click.Option(
["--not-shared-library"],
is_flag=True,
help="Search for photos that are not part of a shared library",
),
"--regex": click.Option(
["--regex"],
metavar="REGEX TEMPLATE",

View File

@ -93,13 +93,25 @@ def debug_dump(
pprint.pprint(photosdb._dbpersons_fullname)
elif attr == "photos":
photos = photosdb.query(options=query_options)
uuid = [photo.uuid for photo in photos]
for uuid_ in uuid:
print(f"_dbphotos['{uuid_}']:")
for p in photos:
# print info on each photo
# catch any errors and continue (because if we're using debug-dump, it might be because of an error)
print(f"photo: {p.uuid}")
print(f"_dbphotos['{p.uuid}']:")
try:
pprint.pprint(photosdb._dbphotos[uuid_])
print(photosdb._dbphotos[p.uuid])
except KeyError:
print(f"Did not find uuid {uuid_} in _dbphotos")
print(f"Did not find uuid {p.uuid} in _dbphotos")
print("PhotoInfo:")
try:
print(p.asdict(shallow=False))
except Exception as e:
print(f"Error dumping PhotoInfo.asdict(): {e}")
print("ZASSET")
print(p.tables().ZASSET.rows_dict())
print("ZADDITIONALASSETATTRIBUTES")
print(p.tables().ZADDITIONALASSETATTRIBUTES.rows_dict())
print("-" * 40)
else:
try:
val = getattr(photosdb, attr)

View File

@ -1,5 +1,7 @@
"""export command for osxphotos CLI"""
from __future__ import annotations
import atexit
import inspect
import os
@ -9,11 +11,12 @@ import shlex
import subprocess
import sys
import time
from typing import Iterable, List, Optional, Tuple
from typing import Any, Callable, Iterable, List, Literal, Optional, Tuple
import click
import osxphotos
import osxphotos.gitignorefile
from osxphotos._constants import (
_EXIF_TOOL_URL,
_OSXPHOTOS_NONE_SENTINEL,
@ -88,9 +91,10 @@ from .common import (
)
from .help import ExportCommand, get_help_msg
from .list import _list_libraries
from .param_types import ExportDBType, FunctionCall, TemplateString
from .param_types import CSVOptions, ExportDBType, FunctionCall, TemplateString
from .report_writer import ReportWriterNoOp, export_report_writer_factory
from .rich_progress import rich_progress
from .sidecar import generate_user_sidecar
from .verbose import get_verbose_console, verbose_print
@ -304,7 +308,8 @@ from .verbose import get_verbose_console, verbose_print
"The resulting file is named photoname.AAE. "
"Note that to import these files back to Photos succesfully, you also need to "
"export the edited photo and match the filename format Photos.app expects: "
"--filename 'IMG_{edited_version?E,}{id:04d}' --edited-suffix ''")
"--filename 'IMG_{edited_version?E,}{id:04d}' --edited-suffix ''",
)
@click.option(
"--sidecar",
default=None,
@ -338,6 +343,53 @@ from .verbose import get_verbose_console, verbose_print
"Warning: this may result in sidecar filename collisions if there are files of different "
"types but the same name in the output directory, e.g. 'IMG_1234.JPG' and 'IMG_1234.MOV'.",
)
@click.option(
"--sidecar-template",
metavar="MAKO_TEMPLATE_FILE SIDECAR_FILENAME_TEMPLATE OPTIONS",
multiple=True,
type=click.Tuple(
[
click.Path(dir_okay=False, file_okay=True, exists=True),
TemplateString(),
CSVOptions(
[
"write_skipped",
"strip_whitespace",
"strip_lines",
"skip_zero",
"catch_errors",
"none",
]
),
]
),
help="Create a custom sidecar file for each photo exported with user provided Mako template (MAKO_TEMPLATE_FILE). "
"MAKO_TEMPLATE_FILE must be a valid Mako template (see https://www.makotemplates.org/). "
"The template will passed the following variables: photo (PhotoInfo object for the photo being exported), "
"sidecar_path (pathlib.Path object for the path to the sidecar being written), and "
"photo_path (pathlib.Path object for the path to the exported photo. "
"SIDECAR_FILENAME_TEMPLATE must be a valid template string (see Templating System in help) "
"which will be rendered to generate the filename of the sidecar file. "
"The `{filepath}` template variable may be used in the SIDECAR_FILENAME_TEMPLATE to refer to the filename of the "
"photo being exported. "
"OPTIONS is a comma-separated list of strings providing additional options to the template. "
"Valid options are: write_skipped, strip_whitespace, strip_lines, skip_zero, catch_errors, none. "
"write_skipped will cause the sidecar file to be written even if the photo is skipped during export. "
"If write_skipped is not passed as an option, the sidecar file will not be written if the photo is skipped during export. "
"strip_whitespace and strip_lines indicate whether or not to strip whitespace and blank lines, respectively, "
"from the resulting sidecar file. "
"skip_zero causes the sidecar file to be skipped if the rendered template is zero-length. "
"catch_errors causes errors in the template to be caught and logged but not raised. "
"Without catch_errors, osxphotos will abort the export if an error occurs in the template. "
"For example, to create a sidecar file with extension .xmp using a template file named 'sidecar.mako' "
"and write a sidecar for skipped photos and strip blank lines but not whitespace: "
"`--sidecar-template sidecar.mako '{filepath}.xmp' write_skipped,strip_lines`. "
"To do the same but to drop the photo extension from the sidecar filename: "
"`--sidecar-template sidecar.mako '{filepath.parent}/{filepath.stem}.xmp' write_skipped,strip_lines`. "
"If you are not passing any options, you must pass 'none' as the last argument to --sidecar-template: "
"`--sidecar-template sidecar.mako '{filepath}.xmp' none`. "
"For an example Mako file see https://raw.githubusercontent.com/RhetTbull/osxphotos/main/examples/custom_sidecar.mako",
)
@click.option(
"--exiftool",
is_flag=True,
@ -561,27 +613,43 @@ from .verbose import get_verbose_console, verbose_print
"For example, photos which had previously been exported and were subsequently deleted in Photos. "
"WARNING: --cleanup will delete *any* files in the export directory that were not exported by osxphotos, "
"for example, your own scripts or other files. Be sure this is what you intend before using "
"--cleanup. Use --dry-run with --cleanup first if you're not certain.",
"--cleanup. Use --dry-run with --cleanup first if you're not certain. "
"To prevent files not generated by osxphotos from being deleted, you may specify one or more rules"
"in a file named `.osxphotos_keep` in the export directory. "
"This file uses the same format as a .gitignore file and should contain one rule per line; "
"lines starting with a `#` will be ignored. "
"Reference https://git-scm.com/docs/gitignore#_pattern_format for details. "
"In addition to the standard .gitignore rules, the rules may also be the absolute path to a file or directory. "
"For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, "
'in the top level of the export directory, you can specify `/*.txt"` in the .osxphotos_keep file. '
"If you want to keep all `.txt` files in the export directory and all subdirectories, "
"you can specify `**/*.txt`. "
"If present, the .osxphotos_keep file will be read after the export is completed and any rules found in the file "
"will be added to the list of rules to keep. "
"See also --keep.",
)
@click.option(
"--keep",
metavar="KEEP_PATH",
metavar="KEEP_RULE",
nargs=1,
multiple=True,
help="When used with --cleanup, prevents file or directory KEEP_PATH from being deleted "
help="When used with --cleanup, prevents file or directory matching KEEP_RULE from being deleted "
"when cleanup is run. Use this if there are files in the export directory that you don't "
"want to be deleted when --cleanup is run. "
"KEEP_PATH may be a file path, e.g. '/Volumes/Photos/keep.jpg', "
"or a file path and wild card, e.g. '/Volumes/Photos/*.txt', "
"or a directory, e.g. '/Volumes/Photos/KeepMe'. "
"KEEP_PATH may be an absolute path or a relative path. "
"If it is relative, it must be relative to the export destination. "
"KEEP_RULE follows the same format rules a .gitignore file. "
"Reference https://git-scm.com/docs/gitignore#_pattern_format for details. "
"In addition to the standard .gitignore rules, KEEP_RULE may also be the absolute path to a file or directory. "
"For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, "
'you can specify `--keep "/Volumes/Photos/*.txt"` or `--keep "*.txt"`. '
"If wild card is used, KEEP_PATH must be enclosed in quotes to prevent the shell from expanding the wildcard, "
'e.g. `--keep "/Volumes/Photos/*.txt"`. '
"If KEEP_PATH is a directory, all files and directories contained in KEEP_PATH will be kept. "
"--keep may be repeated to keep additional files/directories.",
'in the top level of the export directory, you can specify `--keep "/*.txt"`. '
"If you want to keep all `.txt` files in the export directory and all subdirectories, "
'you can specify `--keep "**/*.txt"`. '
"If wild card is used, KEEP_RULE must be enclosed in quotes to prevent the shell from expanding the wildcard. "
"--keep may be repeated to keep additional files/directories. "
"Rules may also be included in a file named `.osxphotos_keep` in the export directory. "
"If present, this file will be read after the export is completed and any rules found in the file "
"will be added to the list of rules to keep. "
"This file uses the same format as a .gitignore file and should contain one rule per line; "
"lines starting with a `#` will be ignored. ",
)
@click.option(
"--add-exported-to-album",
@ -617,11 +685,22 @@ from .verbose import get_verbose_console, verbose_print
"COMMAND is an osxphotos template string, for example: '--post-command exported \"echo {filepath|shell_quote} >> {export_dir}/exported.txt\"', "
"which appends the full path of all exported files to the file 'exported.txt'. "
"You can run more than one command by repeating the '--post-command' option with different arguments. "
"See also --post-command-error and --post-function."
"See Post Command below.",
type=click.Tuple(
[click.Choice(POST_COMMAND_CATEGORIES, case_sensitive=False), TemplateString()]
),
)
@click.option(
"--post-command-error",
metavar="ACTION",
help="Specify either `continue` or `break` for ACTION to control behavior when a post-command fails. "
"If `continue`, osxphotos will log the error and continue processing. "
"If `break`, osxphotos will stop processing any additional --post-command commands for the current photo "
"but will continue with the export. "
"Without --post-command-error, osxphotos will abort the export if a post-command encounters an error. ",
type=click.Choice(["continue", "break"], case_sensitive=False),
)
@click.option(
"--post-function",
metavar="filename.py::function",
@ -844,6 +923,7 @@ def export(
place,
portrait,
post_command,
post_command_error,
post_function,
preview,
preview_if_missing,
@ -863,6 +943,7 @@ def export(
export_aae,
sidecar,
sidecar_drop_ext,
sidecar_template,
skip_bursts,
skip_edited,
skip_live,
@ -894,6 +975,10 @@ def export(
not_syndicated,
saved_to_library,
not_saved_to_library,
shared_moment,
not_shared_moment,
shared_library,
not_shared_library,
selected=False, # Isn't provided on unsupported platforms
# debug, # debug, watch, breakpoint handled in cli/__init__.py
# watch,
@ -994,6 +1079,7 @@ def export(
exiftool_merge_persons = cfg.exiftool_merge_persons
exiftool_option = cfg.exiftool_option
exiftool_path = cfg.exiftool_path
export_aae = cfg.export_aae
export_as_hardlink = cfg.export_as_hardlink
export_by_date = cfg.export_by_date
exportdb = cfg.exportdb
@ -1052,10 +1138,14 @@ def export(
not_panorama = cfg.not_panorama
not_portrait = cfg.not_portrait
not_reference = cfg.not_reference
not_saved_to_library = cfg.not_saved_to_library
not_screenshot = cfg.not_screenshot
not_selfie = cfg.not_selfie
not_shared = cfg.not_shared
not_shared_library = cfg.not_shared_library
not_shared_moment = cfg.not_shared_moment
not_slow_mo = cfg.not_slow_mo
not_syndicated = cfg.not_syndicated
not_time_lapse = cfg.not_time_lapse
only_movies = cfg.only_movies
only_new = cfg.only_new
@ -1069,6 +1159,7 @@ def export(
place = cfg.place
portrait = cfg.portrait
post_command = cfg.post_command
post_command_error = cfg.post_command_error
post_function = cfg.post_function
preview = cfg.preview
preview_if_missing = cfg.preview_if_missing
@ -1081,13 +1172,16 @@ def export(
replace_keywords = cfg.replace_keywords
report = cfg.report
retry = cfg.retry
saved_to_library = cfg.saved_to_library
screenshot = cfg.screenshot
selected = cfg.selected
selfie = cfg.selfie
shared = cfg.shared
export_aae = cfg.export_aae
shared_library = cfg.shared_library
shared_moment = cfg.shared_moment
sidecar = cfg.sidecar
sidecar_drop_ext = cfg.sidecar_drop_ext
sidecar_template = cfg.sidecar_template
skip_bursts = cfg.skip_bursts
skip_edited = cfg.skip_edited
skip_live = cfg.skip_live
@ -1097,6 +1191,7 @@ def export(
skip_uuid_from_file = cfg.skip_uuid_from_file
slow_mo = cfg.slow_mo
strip = cfg.strip
syndicated = cfg.syndicated
theme = cfg.theme
time_lapse = cfg.time_lapse
timestamp = cfg.timestamp
@ -1112,14 +1207,11 @@ def export(
uti = cfg.uti
uuid = cfg.uuid
uuid_from_file = cfg.uuid_from_file
# this is the one option that is named differently in the config file than the variable passed by --verbose (verbose_flag)
verbose_flag = cfg.verbose
verbose_flag = (
cfg.verbose
) # this is named differently in the config file than the variable passed by --verbose (verbose_flag)
xattr_template = cfg.xattr_template
year = cfg.year
syndicated = cfg.syndicated
not_syndicated = cfg.not_syndicated
saved_to_library = cfg.saved_to_library
not_saved_to_library = cfg.not_saved_to_library
# config file might have changed verbose
verbose = verbose_print(verbose=verbose_flag, timestamp=timestamp, theme=theme)
@ -1171,6 +1263,7 @@ def export(
("title", "no_title"),
("syndicated", "not_syndicated"),
("saved_to_library", "not_saved_to_library"),
("shared_moment", "not_shared_moment"),
]
dependent_options = [
("append", ("report")),
@ -1468,18 +1561,30 @@ def export(
kwargs["photo"] = p
kwargs["photo_num"] = photo_num
export_results = export_photo(**kwargs)
if post_function:
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
verbose(f"Calling post-function [bold]{function[1]}")
if not dry_run:
try:
function[0](p, export_results, verbose)
except Exception as e:
rich_echo_error(
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
)
# generate custom sidecars if needed
if sidecar_template:
export_results += generate_user_sidecar(
photo=p,
export_results=export_results,
sidecar_template=sidecar_template,
exiftool_path=exiftool_path,
export_dir=dest,
dry_run=dry_run,
verbose=verbose,
)
# run post functions
if run_results := run_post_function(
photo=p,
post_function=post_function,
export_results=export_results,
verbose=verbose,
dry_run=dry_run,
):
export_results += run_results
# run post command
run_post_command(
photo=p,
post_command=post_command,
@ -1487,7 +1592,7 @@ def export(
export_dir=dest,
dry_run=dry_run,
exiftool_path=exiftool_path,
export_db=export_db,
on_error=post_command_error,
verbose=verbose,
)
@ -1649,6 +1754,7 @@ def export(
if cleanup:
db_file = str(pathlib.Path(export_db_path).resolve())
db_files = [db_file, db_file + "-wal", db_file + "-shm"]
keep_file = str(pathlib.Path(dest) / ".osxphotos_keep")
all_files = (
results.exported
+ results.skipped
@ -1662,35 +1768,46 @@ def export(
+ results.sidecar_exiftool_skipped
+ results.sidecar_xmp_written
+ results.sidecar_xmp_skipped
+ results.sidecar_user_written
+ results.sidecar_user_skipped
+ results.user_written
+ results.user_skipped
# include missing so a file that was already in export directory
# but was missing on --update doesn't get deleted
# (better to have old version than none)
+ results.missing
# include files that have error in case they exist from previous export
+ [r[0] for r in results.error]
# don't delete export database files
+ db_files
# include the .osxphotos_keep file
+ [keep_file]
)
# if --report, add report file to keep list to prevent it from being deleted
if report:
all_files.append(report)
# gather any files that should be kept from both .osxphotos_keep and --keep
dirs_to_keep = []
if keep:
files_to_keep, dirs_to_keep = collect_files_to_keep(keep, dest)
all_files += files_to_keep
files_to_keep, dirs_to_keep = collect_files_to_keep(keep, dest)
all_files += files_to_keep
rich_echo(f"Cleaning up [filepath]{dest}")
cleaned_files, cleaned_dirs = cleanup_files(
dest, all_files, dirs_to_keep, fileutil, verbose=verbose
)
file_str = "files" if len(cleaned_files) != 1 else "file"
dir_str = "directories" if len(cleaned_dirs) != 1 else "directory"
rich_echo(
f"Deleted: [num]{len(cleaned_files)}[/num] {file_str}, [num]{len(cleaned_dirs)}[/num] {dir_str}"
)
report_writer.write(
ExportResults(deleted_files=cleaned_files, deleted_directories=cleaned_dirs)
)
results.deleted_files = cleaned_files
results.deleted_directories = cleaned_dirs
@ -1761,7 +1878,7 @@ def export_photo(
num_photos=1,
tmpdir=None,
update_errors=False,
):
) -> ExportResults:
"""Helper function for export that does the actual export
Args:
@ -1812,6 +1929,7 @@ def export_photo(
use_photos_export: bool; if True forces the use of AppleScript to export even if photo not missing
verbose: callable for verbose output
tmpdir: optional str; temporary directory to use for export
Returns:
list of path(s) of exported photo or None if photo was missing
@ -2184,7 +2302,7 @@ def export_photo_to_directory(
use_photokit,
verbose,
tmpdir,
):
) -> ExportResults:
"""Export photo to directory dest_path"""
results = ExportResults()
@ -2479,22 +2597,40 @@ def collect_files_to_keep(
"""Collect all files to keep for --keep/--cleanup.
Args:
keep: Iterable of filepaths to keep; each path may be a filepath, a filepath/wildcard, or a directory path.
keep: Iterable of patterns to keep; each pattern is a pattern that follows gitignore syntax
export_dir: the export directory which will be used to resolve paths when paths in keep are relative instead of absolute
Returns:
tuple of [files_to_keep], [dirs_to_keep]
"""
export_dir = pathlib.Path(export_dir)
keepers = []
export_dir = pathlib.Path(export_dir).expanduser()
export_dir_str = str(export_dir)
KEEP_RULEs = []
# parse .osxphotos_keep file if it exists
keep_file: pathlib.Path = export_dir / ".osxphotos_keep"
if keep_file.is_file():
for line in keep_file.read_text().splitlines():
line = line.rstrip("\r\n")
KEEP_RULEs.append(line)
# parse any patterns passed via --keep
# do this after the file so negations to the file could be applied via --keep
for k in keep:
keeper = pathlib.Path(k).expanduser()
if not keeper.is_absolute():
# relative path: relative to export_dir
keeper = export_dir / keeper
if keeper.is_dir():
keepers.extend(keeper.glob("**/*"))
keepers.extend(keeper.parent.glob(keeper.name))
if k.startswith(export_dir_str):
# allow full path to be specified for keep (e.g. --keep /path/to/file)
KEEP_RULEs.append(k.replace(export_dir_str, ""))
else:
KEEP_RULEs.append(k)
if not KEEP_RULEs:
return [], []
# have some rules to apply
matcher = osxphotos.gitignorefile.parse_pattern_list(KEEP_RULEs, export_dir)
keepers = []
keepers = [path for path in export_dir.rglob("*") if matcher(path)]
files_to_keep = [str(k) for k in keepers if k.is_file()]
dirs_to_keep = [str(k) for k in keepers if k.is_dir()]
return files_to_keep, dirs_to_keep
@ -2723,20 +2859,52 @@ def write_extended_attributes(
return list(written), [f for f in skipped if f not in written]
def run_post_function(
photo: osxphotos.PhotoInfo,
post_function: tuple[
tuple[
Callable[
[osxphotos.PhotoInfo, ExportResults, Callable[[Any], None]],
None | ExportResults,
],
str,
],
...,
],
export_results: ExportResults,
verbose: Callable[[Any], None],
dry_run: bool,
) -> ExportResults:
"""Run the --post-function functions"""
returned_results = ExportResults()
for function in post_function:
# post function is tuple of (function, filename.py::function_name)
verbose(f"Calling post-function [bold]{function[1]}")
if not dry_run:
try:
if results := function[0](photo, export_results, verbose):
returned_results += results
except Exception as e:
rich_echo_error(
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
)
raise e
return returned_results
def run_post_command(
photo,
post_command,
export_results,
export_dir,
dry_run,
exiftool_path,
export_db,
verbose,
photo: osxphotos.PhotoInfo,
post_command: tuple[tuple[str, str]],
export_results: ExportResults,
export_dir: str | pathlib.Path,
dry_run: bool,
exiftool_path: str,
on_error: Literal["break", "continue"] | None,
verbose: Callable[[Any], None],
):
"""Run --post-command commands"""
# todo: pass in RenderOptions from export? (e.g. so it contains strip, etc?)
# todo: need a shell_quote template type:
# {shell_quote,{filepath}/foo/bar}
# that quotes everything in the default value
for category, command_template in post_command:
files = getattr(export_results, category)
for f in files:
@ -2750,7 +2918,6 @@ def run_post_command(
if command:
verbose(f'Running command: "{command}"')
if not dry_run:
args = shlex.split(command)
cwd = pathlib.Path(f).parent
run_error = None
run_results = None
@ -2759,11 +2926,18 @@ def run_post_command(
except Exception as e:
run_error = e
finally:
run_error = run_error or run_results.returncode
if run_error:
rich_echo_error(
f'[error]Error running command "{command}": {run_error}'
)
returncode = run_results.returncode if run_results else None
if run_error or returncode:
# there was an error running the command
error_str = f'Error running command "{command}": return code: {returncode}, exception: {run_error}'
rich_echo_error(f"[error]{error_str}[/]")
if not on_error:
# no error handling specified, raise exception
raise RuntimeError(error_str)
if on_error == "break":
# break out of loop and return
return
# else on_error must be continue
def render_and_validate_report(report: str, exiftool_path: str, export_dir: str) -> str:

View File

@ -362,7 +362,7 @@ def set_photo_metadata(
photo.title = normalize_unicode(metadata.title)
photo.description = normalize_unicode(metadata.description)
keywords = metadata.keywords.copy()
keywords =normalize_unicode(keywords)
keywords = normalize_unicode(keywords)
if merge_keywords:
if old_keywords := normalize_unicode(photo.keywords):
keywords.extend(old_keywords)

View File

@ -101,6 +101,22 @@ def orphans(ctx, cli_obj, export, db, verbose_flag, timestamp, theme):
)
scan_for_files(directory, uuids_in_library)
# scopes directory (Photos 7+)
if photosdb.photos_version >= 7:
verbose_("Scanning scopes cloudsharing files")
directory = joinpath(
photosdb.library_path, "scopes", "cloudsharing", "resources"
)
scan_for_files(directory, uuids_in_library)
verbose_("Scanning scopes syndication files")
directory = joinpath(photosdb.library_path, "scopes", "syndication")
scan_for_files(directory, uuids_in_library)
verbose_("Scanning scopes momentshared files")
directory = joinpath(photosdb.library_path, "scopes", "momentshared")
scan_for_files(directory, uuids_in_library)
# find orphans
possible_orphans = []
for uuid, files in uuids_in_library.items():

View File

@ -1,4 +1,7 @@
"""Click parameter types for osxphotos CLI"""
from __future__ import annotations
import datetime
import os
import pathlib
@ -18,6 +21,8 @@ from osxphotos.utils import expand_and_validate_filepath, load_function
__all__ = [
"BitMathSize",
"BooleanString",
"CSVOptions",
"DateOffset",
"DateTimeISO8601",
"DeprecatedPath",
@ -302,3 +307,46 @@ class Longitude(click.ParamType):
self.fail(
f"Invalid longitude {value}. Must be a floating point number between -180 and 180."
)
class BooleanString(click.ParamType):
"""A boolean string in the format True/False, Yes/No, T/F, Y/N, 1/0 (case insensitive)"""
name = "BooleanString"
def convert(self, value, param, ctx):
if value.lower() in ["true", "yes", "t", "y", "1"]:
return True
elif value.lower() in ["false", "no", "f", "n", "0"]:
return False
else:
self.fail(
f"Invalid boolean string {value}. Must be one of True/False, Yes/No, T/F, Y/N, 1/0 (case insensitive)."
)
class CSVOptions(click.ParamType):
"""A comma-separated list of option values, not case sensitive"""
name = "CSVOptions"
def __init__(self, options: list[str]):
"""Initialize CSVOptions
Args:
options: list of valid options as str
Note:
The convert method returns a tuple[str, ...] of the options selected
"""
self._csv_options = options
def convert(self, value, param, ctx) -> tuple[str, ...]:
values = value.split(",")
values = [v.lower().strip() for v in values]
for v in values:
if v not in self._csv_options:
self.fail(
f"Invalid option {v}. Must be one of {','.join(self._csv_options)}"
)
return tuple(values)

View File

@ -176,6 +176,18 @@ def inspect_photo(
if photo.moment_info:
properties.append(bold("Moment: ") + f"{photo.moment_info.title or '-'}")
if photo.shared_moment:
info = photo.shared_moment_info
title = info.title if info else "-"
expiry = info.expiry_date.isoformat() if info and info.expiry_date else "-"
share_url = info.share_url if info else "-"
properties.append(
bold("Shared Moment: ") + f"{title} expiry: {expiry} url: {share_url}"
)
if photo.syndicated:
...
if photo.comments:
comments = [f"{c.user}: {c.text}" for c in photo.comments]
properties.append(
@ -260,6 +272,13 @@ def format_flags(photo: PhotoInfo) -> str:
flags.append("in cloud")
if photo.shared:
flags.append("shared")
if photo.syndicated:
flags.append("syndicated") # sourcery skip
flags.append(
"saved to library" if photo.saved_to_library else "not saved to library"
)
if photo.shared_library:
flags.append("shared iCloud library")
flag_str += f"{', '.join(flags) or '-'}"
return flag_str

View File

@ -52,7 +52,9 @@ MACOS_OPTIONS = make_click_option_decorator(
@click.command()
@DB_OPTION
@JSON_OPTION
@click.option("--count", is_flag=True, help="Print count of photos matching query and exit.")
@click.option(
"--count", is_flag=True, help="Print count of photos matching query and exit."
)
@QUERY_OPTIONS
@DELETED_OPTIONS
@MACOS_OPTIONS

View File

@ -98,6 +98,11 @@ class ExportReportWriterCSV(ReportWriterABC):
"cleanup_deleted_file",
"cleanup_deleted_directory",
"exported_album",
"sidecar_user",
"sidecar_user_error",
"user_written",
"user_skipped",
"user_error",
]
mode = "a" if append else "w"
@ -197,9 +202,9 @@ class ExportReportWriterSQLite(ReportWriterABC):
cursor = self._conn.cursor()
cursor.execute(
"INSERT INTO report "
"(datetime, filename, exported, new, updated, skipped, exif_updated, touched, converted_to_jpeg, sidecar_xmp, sidecar_json, sidecar_exiftool, missing, error, exiftool_warning, exiftool_error, extended_attributes_written, extended_attributes_skipped, cleanup_deleted_file, cleanup_deleted_directory, exported_album, report_id) "
"(datetime, filename, exported, new, updated, skipped, exif_updated, touched, converted_to_jpeg, sidecar_xmp, sidecar_json, sidecar_exiftool, missing, error, exiftool_warning, exiftool_error, extended_attributes_written, extended_attributes_skipped, cleanup_deleted_file, cleanup_deleted_directory, exported_album, report_id, sidecar_user, sidecar_user_error, user_written, user_skipped, user_error) " # noqa
"VALUES "
"(:datetime, :filename, :exported, :new, :updated, :skipped, :exif_updated, :touched, :converted_to_jpeg, :sidecar_xmp, :sidecar_json, :sidecar_exiftool, :missing, :error, :exiftool_warning, :exiftool_error, :extended_attributes_written, :extended_attributes_skipped, :cleanup_deleted_file, :cleanup_deleted_directory, :exported_album, :report_id);",
"(:datetime, :filename, :exported, :new, :updated, :skipped, :exif_updated, :touched, :converted_to_jpeg, :sidecar_xmp, :sidecar_json, :sidecar_exiftool, :missing, :error, :exiftool_warning, :exiftool_error, :extended_attributes_written, :extended_attributes_skipped, :cleanup_deleted_file, :cleanup_deleted_directory, :exported_album, :report_id, :sidecar_user, :sidecar_user_error, :user_written, :user_skipped, :user_error);", # noqa
data,
)
self._conn.commit()
@ -262,6 +267,39 @@ class ExportReportWriterSQLite(ReportWriterABC):
self._conn.cursor().execute("ALTER TABLE report ADD COLUMN report_id TEXT;")
self._conn.commit()
# migrate report table and add sidecar_user column if needed (#1123)
if "sidecar_user" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute(
"ALTER TABLE report ADD COLUMN sidecar_user INTEGER;"
)
self._conn.commit()
# migrate report table and add sidecar_user_error if needed (#1123)
if "sidecar_user_error" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute(
"ALTER TABLE report ADD COLUMN sidecar_user_error TEXT;"
)
self._conn.commit()
# migrate report table and add user_written, skipped, error if needed (#1136)
if "user_written" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute(
"ALTER TABLE report ADD COLUMN user_written INTEGER;"
)
self._conn.commit()
if "user_skipped" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute(
"ALTER TABLE report ADD COLUMN user_skipped INTEGER;"
)
self._conn.commit()
if "user_error" not in sqlite_columns(self._conn, "report"):
self._conn.cursor().execute(
"ALTER TABLE report ADD COLUMN user_error TEXT;"
)
self._conn.commit()
# create report_summary view
c.execute(
"""
@ -347,6 +385,11 @@ def prepare_export_results_for_writing(
"cleanup_deleted_file": false,
"cleanup_deleted_directory": false,
"exported_album": "",
"sidecar_user": false,
"sidecar_user_error": "",
"user_written": false,
"user_skipped": false,
"user_error": "",
}
for result in export_results.exported:
@ -421,6 +464,28 @@ def prepare_export_results_for_writing(
for result, album in export_results.exported_album:
all_results[str(result)]["exported_album"] = album
for result in export_results.sidecar_user_written:
all_results[str(result)]["sidecar_user"] = true
all_results[str(result)]["exported"] = true
for result in export_results.sidecar_user_skipped:
all_results[str(result)]["sidecar_user"] = true
all_results[str(result)]["skipped"] = true
for result in export_results.sidecar_user_error:
all_results[str(result[0])]["sidecar_user_error"] = result[1]
for result in export_results.user_written:
all_results[str(result)]["user_written"] = true
all_results[str(result)]["exported"] = true
for result in export_results.user_skipped:
all_results[str(result)]["user_skipped"] = true
all_results[str(result)]["skipped"] = true
for result in export_results.user_error:
all_results[str(result[0])]["user_error"] = result[1]
return all_results

181
osxphotos/cli/sidecar.py Normal file
View File

@ -0,0 +1,181 @@
"""Generate custom sidecar files for use wit `osxphotos export` command and --sidecar-template option"""
from __future__ import annotations
import pathlib
from functools import cache
from typing import Callable
import click
from mako.template import Template
from osxphotos.cli.click_rich_echo import rich_echo_error
from osxphotos.photoexporter import ExportResults
from osxphotos.photoinfo import PhotoInfo
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
@cache
def get_template(template: str) -> Template:
"""Get template from cache or load from file"""
return Template(filename=template)
def generate_user_sidecar(
photo: PhotoInfo,
export_results: ExportResults,
sidecar_template: tuple[tuple[str, str, tuple[str, ...]], ...],
exiftool_path: str,
export_dir: str,
dry_run: bool,
verbose: Callable[..., None],
) -> ExportResults:
"""Generate custom sidecar files for use with `osxphotos export` command and --sidecar-template option
Args:
photo: PhotoInfo object for photo
export_results: ExportResults object
sidecar_template: tuple of (template_file, filename_template) for sidecar template
strip_sidecar: bool, strip whitespace and blank lines from sidecar
exiftool_path: str, path to exiftool
export_dir: str, path to export directory
dry_run: bool, if True, do not actually write sidecar files
verbose: Callable[..., None], verbose logging function
Returns:
ExportResults object with sidecar_user_written and sidecar_user_skipped set
"""
sidecar_results = ExportResults()
for (
template_file,
filename_template,
options,
) in sidecar_template:
strip_whitespace = "strip_whitespace" in options
strip_lines = "strip_lines" in options
write_skipped = "write_skipped" in options
skip_zero = "skip_zero" in options
catch_errors = "catch_errors" in options
if not write_skipped:
# skip writing sidecar if photo not exported
# but if run with --update and --cleanup, a sidecar file may have been written
# in the past, so check if it exists and if so keep it
for filepath in export_results.skipped:
template_filename = _render_sidecar_filename(
photo=photo,
filepath=filepath,
filename_template=filename_template,
export_dir=export_dir,
exiftool_path=exiftool_path,
)
if template_filename and pathlib.Path(template_filename).exists():
verbose(
f"Skipping existing sidecar file [filepath]{template_filename}[/]"
)
sidecar_results.sidecar_user_skipped.append(template_filename)
# write sidecar files for exported and missing files (and skipped if write_skipped)
files_to_process = export_results.exported + export_results.missing
if write_skipped:
files_to_process += export_results.skipped
for filepath in files_to_process:
template_filename = _render_sidecar_filename(
photo=photo,
filepath=filepath,
filename_template=filename_template,
export_dir=export_dir,
exiftool_path=exiftool_path,
)
if not template_filename:
raise click.BadOptionUsage(
f"Invalid SIDECAR_FILENAME_TEMPLATE for --sidecar-template '{filename_template}'"
)
verbose(f"Writing sidecar file [filepath]{template_filename}[/]")
if error := _render_sidecar_and_write_data(
template_file=template_file,
photo=photo,
template_filename=template_filename,
filepath=filepath,
strip_whitespace=strip_whitespace,
strip_lines=strip_lines,
skip_zero=skip_zero,
catch_errors=catch_errors,
verbose=verbose,
dry_run=dry_run,
):
sidecar_results.sidecar_user_error.append((template_filename, error))
else:
sidecar_results.sidecar_user_written.append(template_filename)
print(sidecar_results)
return sidecar_results
def _render_sidecar_filename(
photo: PhotoInfo,
filepath: str,
filename_template: str,
export_dir: str,
exiftool_path: str,
):
"""Render sidecar filename template"""
render_options = RenderOptions(export_dir=export_dir, filepath=filepath)
photo_template = PhotoTemplate(photo, exiftool_path=exiftool_path)
template_filename, _ = photo_template.render(
filename_template, options=render_options
)
template_filename = template_filename[0] if template_filename else None
return template_filename
def _render_sidecar_and_write_data(
template_file: str,
photo: PhotoInfo,
template_filename: str,
filepath: str,
strip_whitespace: bool,
strip_lines: bool,
skip_zero: bool,
catch_errors: bool,
verbose: Callable[..., None],
dry_run: bool,
) -> Exception | None:
"""Render sidecar template and write data to file
Returns:
None if no errors, otherwise Exception if catch_errors is True
If catch_errors is False, raises exception if error
"""
sidecar = get_template(template_file)
try:
sidecar_data = sidecar.render(
photo=photo,
sidecar_path=pathlib.Path(template_filename),
photo_path=pathlib.Path(filepath),
)
except Exception as e:
if catch_errors:
rich_echo_error(f"[error]Error rendering sidecar template: {e}[/]")
return e
raise e
if strip_whitespace:
# strip whitespace
sidecar_data = "\n".join(line.strip() for line in sidecar_data.split("\n"))
if strip_lines:
# strip blank lines
sidecar_data = "\n".join(
line for line in sidecar_data.split("\n") if line.strip()
)
if not dry_run:
# write sidecar file
if skip_zero and not sidecar_data:
verbose(f"Skipping empty sidecar file [filepath]{template_filename}[/]")
return
with open(template_filename, "w") as f:
f.write(sidecar_data)
return None

View File

@ -219,7 +219,7 @@ def _verbose_print_function(
styled_args = []
timestamp_str = f"{str(datetime.now())} -- " if timestamp else ""
for arg in args:
if type(arg) == str:
if isinstance(arg, str):
arg = timestamp_str + arg
if "error" in arg.lower():
arg = click.style(arg, fg=CLI_COLOR_ERROR)
@ -236,7 +236,7 @@ def _verbose_print_function(
timestamp_str = time_stamp() if timestamp else ""
new_args = []
for arg in args:
if type(arg) == str:
if isinstance(arg, str):
if "error" in arg.lower():
arg = f"[error]{arg}"
if ERROR_EMOJI:
@ -257,7 +257,7 @@ def _verbose_print_function(
timestamp_str = time_stamp() if timestamp else ""
new_args = []
for arg in args:
if type(arg) == str:
if isinstance(arg, str):
if "error" in arg.lower():
arg = f"[error]{arg}"
if ERROR_EMOJI:

Binary file not shown.

View File

@ -58,7 +58,7 @@ def exiftool_can_write(suffix: str) -> bool:
def escape_str(s):
"""escape string for use with exiftool -E"""
if type(s) != str:
if not isinstance(s, str):
return s
s = html.escape(s)
s = s.replace("\n", "&#xa;")
@ -69,7 +69,7 @@ def escape_str(s):
def unescape_str(s):
"""unescape an HTML string returned by exiftool -E"""
if type(s) != str:
if not isinstance(s, str):
return s
# avoid " in values which result in json.loads() throwing an exception, #636
s = s.replace("&quot;", '\\"')

431
osxphotos/gitignorefile.py Normal file
View File

@ -0,0 +1,431 @@
"""A spec-compliant `.gitignore` parser for Python.
Versioned from: https://github.com/excitoon/gitignorefile to add parse_pattern_list() function
to apply .gitignore rules to a list of patterns that aren't actually a .gitignore file.
The original code was licensed under the MIT license, Copyright (c) 2022 Vladimir Chebotarev
"""
from __future__ import annotations
import os
import re
from typing import Callable
DEFAULT_IGNORE_NAMES = [".gitignore", ".git/info/exclude"]
def parse_pattern_list(
patterns: list[str], base_path: str = None
) -> Callable[[str], bool]:
"""Parse a list of patterns and return a callable to match against a path.
Args:
patterns (list[str]): List of patterns to match against.
base_path (str): Base path for applying ignore rules.
Returns:
Callable[[str], bool]: Callable which returns `True` if specified path is ignored.
You can also pass `is_dir: bool` optional parameter if you know whether the specified path is a directory.
"""
rules = []
for pattern in patterns:
pattern = pattern.rstrip("\r\n")
if rule := _rule_from_pattern(pattern):
rules.append(rule)
return _IgnoreRules(rules, base_path).match
def parse(path, base_path=None):
"""Parses single `.gitignore` file.
Args:
path (str): Path to `.gitignore` file.
base_path (str): Base path for applying ignore rules.
Returns:
Callable[[str], bool]: Callable which returns `True` if specified path is ignored.
You can also pass `is_dir: bool` optional parameter if you know whether the specified path is a directory.
"""
if base_path is None:
base_path = os.path.dirname(path) or os.path.dirname(os.path.abspath(path))
rules = []
with open(path) as ignore_file:
for line in ignore_file:
line = line.rstrip("\r\n")
if rule := _rule_from_pattern(line):
rules.append(rule)
return _IgnoreRules(rules, base_path).match
def ignore(ignore_names=DEFAULT_IGNORE_NAMES):
"""Returns `shutil.copytree()`-compatible ignore function for skipping ignored files.
It will check if file is ignored by any `.gitignore` in the directory tree.
Args:
ignore_names (list[str], optional): List of names of ignore files.
Returns:
Callable[[str, list[str]], list[str]]: Callable compatible with `shutil.copytree()`.
"""
matches = Cache(ignore_names=ignore_names)
return lambda root, names: {
name for name in names if matches(os.path.join(root, name))
}
def ignored(path, is_dir=None, ignore_names=DEFAULT_IGNORE_NAMES):
"""Checks if file is ignored by any `.gitignore` in the directory tree.
Args:
path (str): Path to check against ignore rules.
is_dir (bool, optional): Set if you know whether the specified path is a directory.
ignore_names (list[str], optional): List of names of ignore files.
Returns:
bool: `True` if the path is ignored.
"""
return Cache(ignore_names=ignore_names)(path, is_dir=is_dir)
class Cache:
"""Caches information about different `.gitignore` files in the directory tree.
Allows to reduce number of queries to filesystem to mininum.
"""
def __init__(self, ignore_names=DEFAULT_IGNORE_NAMES):
"""Constructs `Cache` objects.
Args:
ignore_names (list[str], optional): List of names of ignore files.
"""
self.__ignore_names = ignore_names
self.__gitignores = {}
def __call__(self, path, is_dir=None):
"""Checks whether the specified path is ignored.
Args:
path (str): Path to check against ignore rules.
is_dir (bool, optional): Set if you know whether the specified path is a directory.
"""
path = _Path(path)
add_to_children = {}
plain_paths = []
for parent in path.parents():
if parent.parts in self.__gitignores:
break
ignore_paths = []
for ignore_name in self.__ignore_names:
ignore_path = parent.join(ignore_name)
if ignore_path.isfile():
ignore_paths.append(str(ignore_path))
if ignore_paths:
matches = [
parse(ignore_path, base_path=parent) for ignore_path in ignore_paths
]
add_to_children[parent] = (matches, plain_paths)
plain_paths = []
else:
plain_paths.append(parent)
else:
parent = _Path(tuple()) # Null path.
self.__gitignores[parent.parts] = []
for plain_path in plain_paths:
# assert plain_path.parts not in self.__gitignores
self.__gitignores[plain_path.parts] = self.__gitignores[parent.parts]
for parent, (_, parent_plain_paths) in reversed(list(add_to_children.items())):
# assert parent.parts not in self.__gitignores
self.__gitignores[parent.parts] = self.__gitignores[
parent.parts[:-1]
].copy()
for parent_to_add, (gitignores_to_add, _) in reversed(
list(add_to_children.items())
):
self.__gitignores[parent.parts].extend(gitignores_to_add)
if parent_to_add == parent:
break
self.__gitignores[parent.parts].reverse()
for plain_path in parent_plain_paths:
# assert plain_path.parts not in self.__gitignores
self.__gitignores[plain_path.parts] = self.__gitignores[parent.parts]
# This parent comes either from first or second loop.
return any((m(path, is_dir=is_dir) for m in self.__gitignores[parent.parts]))
class _Path:
def __init__(self, path):
if isinstance(path, (str, bytes, os.PathLike)):
abs_path = os.path.abspath(path)
self.__parts = tuple(_path_split(abs_path))
self.__joined = abs_path
self.__is_dir = None
else:
self.__parts = path
self.__joined = None
self.__is_dir = None
@property
def parts(self):
return self.__parts
def join(self, name):
return _Path(self.__parts + (name,))
def relpath(self, base_path):
if self.__parts[: len(base_path.__parts)] == base_path.__parts:
return "/".join(self.__parts[len(base_path.__parts) :])
else:
return None
def parents(self):
for i in range(len(self.__parts) - 1, 0, -1):
yield _Path(self.__parts[:i])
def isfile(self):
return os.path.isfile(str(self))
def isdir(self):
if self.__is_dir is not None:
return self.__is_dir
self.__is_dir = os.path.isdir(str(self))
return self.__is_dir
def __str__(self):
if self.__joined is None:
self.__joined = (
os.sep.join(self.__parts) if self.__parts != ("",) else os.sep
)
return self.__joined
def _rule_from_pattern(pattern):
# Takes a `.gitignore` match pattern, such as "*.py[cod]" or "**/*.bak",
# and returns an `_IgnoreRule` suitable for matching against files and
# directories. Patterns which do not match files, such as comments
# and blank lines, will return `None`.
# Early returns follow
# Discard comments and separators
if not pattern.lstrip() or pattern.lstrip().startswith("#"):
return
# Discard anything with more than two consecutive asterisks
if "***" in pattern:
return
# Strip leading bang before examining double asterisks
if pattern.startswith("!"):
negation = True
pattern = pattern[1:]
else:
negation = False
# Discard anything with invalid double-asterisks -- they can appear
# at the start or the end, or be surrounded by slashes
for m in re.finditer("\\*\\*", pattern):
start_index = m.start()
if (
start_index != 0
and start_index != len(pattern) - 2
and (pattern[start_index - 1] != "/" or pattern[start_index + 2] != "/")
):
return
# Special-casing '/', which doesn't match any files or directories
if pattern.rstrip() == "/":
return
directory_only = pattern.endswith("/")
# A slash is a sign that we're tied to the `base_path` of our rule
# set.
anchored = "/" in pattern[:-1]
if pattern.startswith("/"):
pattern = pattern[1:]
if pattern.startswith("**"):
pattern = pattern[2:]
anchored = False
if pattern.startswith("/"):
pattern = pattern[1:]
if pattern.endswith("/"):
pattern = pattern[:-1]
# patterns with leading hashes are escaped with a backslash in front, unescape it
if pattern.startswith("\\#"):
pattern = pattern[1:]
# trailing spaces are ignored unless they are escaped with a backslash
i = len(pattern) - 1
striptrailingspaces = True
while i > 1 and pattern[i] == " ":
if pattern[i - 1] == "\\":
pattern = pattern[: i - 1] + pattern[i:]
i -= 1
striptrailingspaces = False
else:
if striptrailingspaces:
pattern = pattern[:i]
i -= 1
regexp = _fnmatch_pathname_to_regexp(pattern, anchored, directory_only)
return _IgnoreRule(regexp, negation, directory_only)
class _IgnoreRules:
def __init__(self, rules, base_path):
self.__rules = rules
self.__can_return_immediately = not any((r.negation for r in rules))
self.__base_path = (
_Path(base_path) if not isinstance(base_path, _Path) else base_path
)
def match(self, path, is_dir=None):
if not isinstance(path, _Path):
path = _Path(path)
rel_path = path.relpath(self.__base_path)
if rel_path is not None:
if is_dir is None:
is_dir = path.isdir() # TODO Pass callable here.
if self.__can_return_immediately:
return any((r.match(rel_path, is_dir) for r in self.__rules))
else:
matched = False
for rule in self.__rules:
if rule.match(rel_path, is_dir):
matched = not rule.negation
else:
return matched
else:
return False
class _IgnoreRule:
def __init__(self, regexp, negation, directory_only):
self.__regexp = re.compile(regexp)
self.__negation = negation
self.__directory_only = directory_only
self.__match = self.__regexp.match
@property
def regexp(self):
return self.__regexp
@property
def negation(self):
return self.__negation
def match(self, rel_path, is_dir):
m = self.__match(rel_path)
# If we need a directory, check there is something after slash and if there is not, target must be a directory.
# If there is something after slash then it's a directory irrelevant to type of target.
# `self.directory_only` implies we have group number 1.
# N.B. Question mark inside a group without a name can shift indices. :(
return m and (not self.__directory_only or m.group(1) is not None or is_dir)
if os.altsep is not None:
_all_seps_expr = f"[{re.escape(os.sep)}{re.escape(os.altsep)}]"
_path_split = lambda path: re.split(_all_seps_expr, path) # noqa: E731
else:
_path_split = lambda path: path.split(os.sep) # noqa: E731
def _fnmatch_pathname_to_regexp(pattern, anchored, directory_only):
# Implements `fnmatch` style-behavior, as though with `FNM_PATHNAME` flagged;
# the path separator will not match shell-style `*` and `.` wildcards.
# Frustratingly, python's fnmatch doesn't provide the FNM_PATHNAME
# option that `.gitignore`'s behavior depends on.
if not pattern:
if directory_only:
return "[^/]+(/.+)?$" # Empty name means no path fragment.
else:
return ".*"
i, n = 0, len(pattern)
res = ["(?:^|.+/)" if not anchored else ""]
while i < n:
c = pattern[i]
i += 1
if c == "*":
if i < n and pattern[i] == "*":
i += 1
if i < n and pattern[i] == "/":
i += 1
res.append("(.+/)?") # `/**/` matches `/`.
else:
res.append(".*")
else:
res.append("[^/]*")
elif c == "?":
res.append("[^/]")
elif c == "[":
j = i
if j < n and pattern[j] == "!":
j += 1
if j < n and pattern[j] == "]":
j += 1
while j < n and pattern[j] != "]":
j += 1
if j >= n:
res.append("\\[")
else:
stuff = pattern[i:j].replace("\\", "\\\\")
i = j + 1
if stuff[0] == "!":
stuff = f"^{stuff[1:]}"
elif stuff[0] == "^":
stuff = f"\\{stuff}"
res.append(f"[{stuff}]")
else:
res.append(re.escape(c))
if (
directory_only
): # In this case we are interested if there is something after slash.
res.append("(/.+)?$")
else:
res.append("(?:/.+)?$")
return "".join(res)

View File

@ -266,7 +266,51 @@ class StagedFiles:
class ExportResults:
"""Results class which holds export results for export"""
"""Results class which holds export results for export
Args:
converted_to_jpeg: list of files converted to jpeg
deleted_directories: list of directories deleted
deleted_files: list of files deleted
error: list of tuples of (filename, error) for any errors generated during export
exif_updated: list of files where exif data was updated with exiftool
exiftool_error: list of tuples of (filename, error) for any errors generated by exiftool
exiftool_warning: list of tuples of (filename, warning) for any warnings generated by exiftool
exported: list of files exported
exported_album: list of tuples of (file, album) for any files exported to an album
metadata_changed: list of filenames that had metadata changes since last export
missing: list of files that were missing
missing_album: list of tuples of (file, album) for any files that were missing from an album
new: list of files that were new
aae_written: list of files where .AAE file was written
sidecar_exiftool_skipped: list of files where exiftool sidecar was skipped
sidecar_exiftool_written: list of files where exiftool sidecar was written
sidecar_json_skipped: list of files where json sidecar was skipped
sidecar_json_written: list of files where json sidecar was written
sidecar_xmp_skipped: list of files where xmp sidecar was skipped
sidecar_xmp_written: list of files where xmp sidecar was written
sidecar_user_written: list of files where user sidecar was written
sidecar_user_skipped: list of files where user sidecar was skipped
sidecar_user_error: list of tuples of (filename, error) for any errors generated by user sidecar
skipped: list of files that were skipped
skipped_album: list of tuples of (file, album) for any files that were skipped from an album
to_touch: list of files that were touched
touched: list of files that were touched
updated: list of files that were updated
xattr_skipped: list of files where xattr was skipped
xattr_written: list of files where xattr was written
user_written: list of files written by user post_function
user_skipped: list of files skipped by user post_function
user_error: list of tuples of (filename, error) for any errors generated by user post_function
Notes:
Each attribute is a list of files or None if no files for that attribute.
Error and warning attributes are a list of tuples of (filename, error) where filename is the file that caused the error and error is the error message.
Album attributes are a list of tuples of (file, album) where file is the file exported and album is the album it was exported to.
ExportResults can be added together with the += operator to combine results as the export progresses.
"""
# Note: __init__ docs above added in the class docstring so they are picked up by sphinx
__slots__ = [
"_datetime",
@ -290,6 +334,9 @@ class ExportResults:
"sidecar_json_written",
"sidecar_xmp_skipped",
"sidecar_xmp_written",
"sidecar_user_written",
"sidecar_user_skipped",
"sidecar_user_error",
"skipped",
"skipped_album",
"to_touch",
@ -297,38 +344,51 @@ class ExportResults:
"updated",
"xattr_skipped",
"xattr_written",
"user_written",
"user_skipped",
"user_error",
]
def __init__(
self,
converted_to_jpeg=None,
deleted_directories=None,
deleted_files=None,
error=None,
exif_updated=None,
exiftool_error=None,
exiftool_warning=None,
exported=None,
exported_album=None,
metadata_changed=None,
missing=None,
missing_album=None,
new=None,
aae_written=None,
sidecar_exiftool_skipped=None,
sidecar_exiftool_written=None,
sidecar_json_skipped=None,
sidecar_json_written=None,
sidecar_xmp_skipped=None,
sidecar_xmp_written=None,
skipped=None,
skipped_album=None,
to_touch=None,
touched=None,
updated=None,
xattr_skipped=None,
xattr_written=None,
converted_to_jpeg: list[str] | None = None,
deleted_directories: list[str] | None = None,
deleted_files: list[str] | None = None,
error: list[str] | None = None,
exif_updated: list[str] | None = None,
exiftool_error: list[tuple[str, str]] | None = None,
exiftool_warning: list[tuple[str, str]] | None = None,
exported: list[str] | None = None,
exported_album: list[tuple[str, str]] | None = None,
metadata_changed: list[str] | None = None,
missing: list[str] | None = None,
missing_album: list[tuple[str, str]] | None = None,
new: list[str] | None = None,
aae_written: list[str] | None = None,
sidecar_exiftool_skipped: list[str] | None = None,
sidecar_exiftool_written: list[str] | None = None,
sidecar_json_skipped: list[str] | None = None,
sidecar_json_written: list[str] | None = None,
sidecar_xmp_skipped: list[str] | None = None,
sidecar_xmp_written: list[str] | None = None,
sidecar_user_written: list[str] | None = None,
sidecar_user_skipped: list[str] | None = None,
sidecar_user_error: list[tuple[str, str]] | None = None,
skipped: list[str] | None = None,
skipped_album: list[tuple[str, str]] | None = None,
to_touch: list[str] | None = None,
touched: list[str] | None = None,
updated: list[str] | None = None,
xattr_skipped: list[str] | None = None,
xattr_written: list[str] | None = None,
user_written: list[str] | None = None,
user_skipped: list[str] | None = None,
user_error: list[tuple[str, str]] | None = None,
):
"""ExportResults data class to hold results of export.
See class docstring for details.
"""
local_vars = locals()
self._datetime = datetime.now().isoformat()
for attr in self.attributes:
@ -361,11 +421,17 @@ class ExportResults:
+ self.sidecar_exiftool_skipped
+ self.sidecar_xmp_written
+ self.sidecar_xmp_skipped
+ self.sidecar_user_written
+ self.sidecar_user_skipped
+ self.missing
+ self.user_written
+ self.user_skipped
)
files += [x[0] for x in self.exiftool_warning]
files += [x[0] for x in self.exiftool_error]
files += [x[0] for x in self.error]
files += [x[0] for x in self.sidecar_user_error]
files += [x[0] for x in self.user_error]
return list(set(files))
@ -1433,7 +1499,8 @@ class PhotoExporter:
if options.export_as_hardlink:
try:
if aae_dest.exists() and any(
[options.overwrite, options.update, options.force_update]):
[options.overwrite, options.update, options.force_update]
):
try:
options.fileutil.unlink(aae_dest)
except Exception as e:

View File

@ -65,6 +65,8 @@ from .platform import assert_macos, is_macos
from .query_builder import get_query
from .scoreinfo import ScoreInfo
from .searchinfo import SearchInfo
from .shareinfo import ShareInfo, get_moment_share_info, get_share_info
from .shareparticipant import ShareParticipant, get_share_participants
from .uti import get_preferred_uti_extension, get_uti_for_extension
from .utils import _get_resource_loc, hexdigest, list_directory
@ -173,6 +175,16 @@ class PhotoInfo:
"""Returns candidate path for original photo on Photos >= version 5"""
if self._info["shared"]:
return self._path_5_shared()
if (
self.shared_moment
and self._db.photos_version >= 7
and self._path_shared_moment()
):
# path for photos in shared moments if it's in the shared moment folder
# the file may also be in the originals folder which the next check will catch
# check shared_moment first as a photo can be both a shared moment and syndicated
# and if so, will be in the shared moment folder
return self._path_shared_moment()
if self.syndicated and not self.saved_to_library:
# path for "shared with you" syndicated photos that have not yet been saved to the library
return self._path_syndication()
@ -216,8 +228,8 @@ class PhotoInfo:
)
def _path_syndication(self):
"""Return path for syndicated photo on Photos >= version 8"""
# Photos 8+ stores syndicated photos in a separate directory
"""Return path for syndicated photo on Photos >= version 7"""
# Photos 7+ stores syndicated photos in a separate directory
# in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID.ext
# where X is first digit of UUID
syndication_path = "scopes/syndication/originals"
@ -230,6 +242,21 @@ class PhotoInfo:
)
return path if os.path.isfile(path) else None
def _path_shared_moment(self):
"""Return path for shared moment photo on Photos >= version 7"""
# Photos 7+ stores shared moment photos in a separate directory
# in ~/Photos Library.photoslibrary/scopes/momentshared/originals/X/UUID.ext
# where X is first digit of UUID
momentshared_path = "scopes/momentshared/originals"
uuid_dir = self.uuid[0]
path = os.path.join(
self._db._library_path,
momentshared_path,
uuid_dir,
self.filename,
)
return path if os.path.isfile(path) else None
def _path_4(self):
"""Returns candidate path for original photo on Photos <= version 4"""
if self._info["has_raw"]:
@ -350,7 +377,7 @@ class PhotoInfo:
)
def _path_edited_4(self) -> str | None:
"""return path_edited for Photos <= 4; modified version of code in PhotoInfo to debug #859"""
"""return path_edited for Photos <= 4; #859"""
if not self._info["hasAdjustments"]:
return None
@ -455,7 +482,7 @@ class PhotoInfo:
# In Photos 5, raw is in same folder as original but with _4.ext
# Unless "Copy Items to the Photos Library" is not checked
# then RAW image is not renamed but has same name is jpeg buth with raw extension
# then RAW image is not renamed but has same name is jpeg but with raw extension
# Current implementation finds images with the correct raw UTI extension
# in same folder as the original and with same stem as original in form: original_stem*.raw_ext
# TODO: I don't like this -- would prefer a more deterministic approach but until I have more
@ -525,6 +552,7 @@ class PhotoInfo:
f"MISSING PATH: RAW photo for UUID {self._uuid} should be at {photopath} but does not appear to exist"
)
photopath = None
return photopath
@property
def description(self):
@ -910,6 +938,8 @@ class PhotoInfo:
elif self.live_photo and self.path and not self.ismissing:
if self.shared:
return self._path_live_photo_shared_5()
if self.shared_moment and self._db.photos_version >= 7:
return self._path_live_shared_moment()
if self.syndicated and not self.saved_to_library:
# syndicated ("Shared with you") photos not yet saved to library
return self._path_live_syndicated()
@ -972,8 +1002,8 @@ class PhotoInfo:
return photopath
def _path_live_syndicated(self):
"""Return path for live syndicated photo on Photos >= version 8"""
# Photos 8+ stores live syndicated photos in a separate directory
"""Return path for live syndicated photo on Photos >= version 7"""
# Photos 7+ stores live syndicated photos in a separate directory
# in ~/Photos Library.photoslibrary/scopes/syndication/originals/X/UUID_3.mov
# where X is first digit of UUID
syndication_path = "scopes/syndication/originals"
@ -987,6 +1017,22 @@ class PhotoInfo:
)
return live_photo if os.path.isfile(live_photo) else None
def _path_live_shared_moment(self):
"""Return path for live shared moment photo on Photos >= version 7"""
# Photos 7+ stores live shared moment photos in a separate directory
# in ~/Photos Library.photoslibrary/scopes/momentshared/originals/X/UUID_3.mov
# where X is first digit of UUID
shared_moment_path = "scopes/momentshared/originals"
uuid_dir = self.uuid[0]
filename = f"{pathlib.Path(self.filename).stem}_3.mov"
live_photo = os.path.join(
self._db._library_path,
shared_moment_path,
uuid_dir,
filename,
)
return live_photo if os.path.isfile(live_photo) else None
@cached_property
def path_derivatives(self) -> list[str]:
"""Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)"""
@ -997,19 +1043,29 @@ class PhotoInfo:
return self._path_derivatives_5_shared()
directory = self._uuid[0] # first char of uuid
if self.syndicated and not self.saved_to_library:
if self.shared_moment and self._db.photos_version >= 7:
# shared moments
derivative_path = "scopes/momentshared/resources/derivatives"
thumb_path = (
f"{derivative_path}/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
)
elif self.syndicated and not self.saved_to_library:
# syndicated ("Shared with you") photos not yet saved to library
derivative_path = "scopes/syndication/resources/derivatives"
thumb_path = (
f"{derivative_path}/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
)
else:
derivative_path = f"resources/derivatives/{directory}"
derivative_path = "resources/derivatives"
thumb_path = (
f"resources/derivatives/masters/{directory}/{self.uuid}_4_5005_c.jpeg"
)
derivative_path = pathlib.Path(self._db._library_path).joinpath(derivative_path)
derivative_path = (
pathlib.Path(self._db._library_path)
.joinpath(derivative_path)
.joinpath(directory)
)
thumb_path = pathlib.Path(self._db._library_path).joinpath(thumb_path)
# find all files that start with uuid in derivative path
@ -1349,9 +1405,9 @@ class PhotoInfo:
def syndicated(self) -> bool | None:
"""Return true if photo was shared via syndication (e.g. via Messages, etc.);
these are photos that appear in "Shared with you" album.
Photos 8+ only; returns None if not Photos 8+.
Photos 7+ only; returns None if not Photos 7+.
"""
if self._db.photos_version < 8:
if self._db.photos_version < 7:
return None
try:
@ -1366,10 +1422,10 @@ class PhotoInfo:
def saved_to_library(self) -> bool | None:
"""Return True if syndicated photo has been saved to library;
returns False if photo is not syndicated or has not been saved to the library.
Returns None if not Photos 8+.
Syndicated photos are photos that appear in "Shared with you" album; Photos 8+ only.
Returns None if not Photos 7+.
Syndicated photos are photos that appear in "Shared with you" album; Photos 7+ only.
"""
if self._db.photos_version < 8:
if self._db.photos_version < 7:
return None
try:
@ -1377,6 +1433,46 @@ class PhotoInfo:
except KeyError:
return False
@cached_property
def shared_moment(self) -> bool:
"""Returns True if photo is part of a shared moment otherwise False (Photos 7+ only)"""
return bool(self._info["moment_share"])
@cached_property
def shared_moment_info(self) -> ShareInfo | None:
"""Returns ShareInfo object with information about the shared moment the photo is part of (Photos 7+ only)"""
if self._db.photos_version < 7:
return None
try:
return get_moment_share_info(self._db, self.uuid)
except ValueError:
return None
@cached_property
def share_info(self) -> ShareInfo | None:
"""Returns ShareInfo object with information about the shared photo in a shared iCloud library (Photos 8+ only) (currently experimental)"""
if self._db.photos_version < 8:
return None
try:
return get_share_info(self._db, self.uuid)
except ValueError:
return None
@cached_property
def shared_library(self) -> bool:
"""Returns True if photo is in a shared iCloud library otherwise False (Photos 8+ only)"""
# TODO: this is just a guess right now as I don't currently use shared libraries
return bool(self._info["active_library_participation_state"])
@cached_property
def share_participants(self) -> list[ShareParticipant]:
"""Returns list of ShareParticpant objects with information on who the photo is shared with (Photos 8+ only)"""
if self._db.photos_version < 8:
return []
return get_share_participants(self._db, self.uuid)
@property
def labels(self):
"""returns list of labels applied to photo by Photos image categorization
@ -1879,6 +1975,10 @@ class PhotoInfo:
place = self.place.asdict() if self.place else {}
score = dataclasses.asdict(self.score) if self.score else {}
# do not add any new properties to data_dict as this is used by export to determine
# if a photo needs to be re-exported and adding new properties may cause all photos
# to be re-exported
# see below `if not shallow:`
dict_data = {
"albums": self.albums,
"burst": self.burst,
@ -1953,6 +2053,7 @@ class PhotoInfo:
}
# non-shallow keys
# add any new properties here
if not shallow:
dict_data["album_info"] = [album.asdict() for album in self.album_info]
dict_data["path_derivatives"] = self.path_derivatives
@ -1980,6 +2081,10 @@ class PhotoInfo:
if self.search_info_normalized
else {}
)
dict_data["syndicated"] = self.syndicated
dict_data["saved_to_library"] = self.saved_to_library
dict_data["shared_moment"] = self.shared_moment
dict_data["shared_library"] = self.shared_library
return dict_data

View File

@ -4,4 +4,8 @@ Processes a Photos.app library database to extract information about photos
"""
from .photosdb import PhotosDB
from .photosdb_utils import get_db_model_version, get_db_version, get_model_version
from .photosdb_utils import (
get_db_version,
get_model_version,
get_photos_version_from_model,
)

View File

@ -179,6 +179,8 @@ def _process_faceinfo_5(photosdb):
db = photosdb._tmp_db
asset_table = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
asset_fk = _DB_TABLE_NAMES[photosdb._photos_ver]["DETECTED_FACE_ASSET_FK"]
person_fk = _DB_TABLE_NAMES[photosdb._photos_ver]["DETECTED_FACE_PERSON_FK"]
(conn, cursor) = sqlite_open_ro(db)
@ -188,7 +190,7 @@ def _process_faceinfo_5(photosdb):
ZDETECTEDFACE.Z_PK,
{asset_table}.ZUUID,
ZDETECTEDFACE.ZUUID,
ZDETECTEDFACE.ZPERSON,
{person_fk},
ZPERSON.ZFULLNAME,
ZDETECTEDFACE.ZAGETYPE,
NULL, -- ZDETECTEDFACE.ZBALDTYPE (Removed in Monterey)
@ -225,8 +227,8 @@ def _process_faceinfo_5(photosdb):
NULL, -- ZDETECTEDFACE.ZYAW,
ZDETECTEDFACE.ZMASTERIDENTIFIER
FROM ZDETECTEDFACE
JOIN {asset_table} ON {asset_table}.Z_PK = ZDETECTEDFACE.ZASSET
JOIN ZPERSON ON ZPERSON.Z_PK = ZDETECTEDFACE.ZPERSON;
JOIN {asset_table} ON {asset_table}.Z_PK = {asset_fk}
JOIN ZPERSON ON ZPERSON.Z_PK = {person_fk};
"""
)

View File

@ -0,0 +1,62 @@
""" Methods for PhotosDB to process shared iCloud library data (#860)"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from .._constants import _DB_TABLE_NAMES, _PHOTOS_SHARED_LIBRARY_VERSION
from ..sqlite_utils import sqlite_open_ro
if TYPE_CHECKING:
from osxphotos.photosdb import PhotosDB
logger = logging.getLogger("osxphotos")
def _process_shared_library_info(self: PhotosDB):
"""Process syndication information"""
if self.photos_version < 7:
raise NotImplementedError(
f"syndication info not implemented for this database version: {self.photos_version}"
)
if self._model_ver < _PHOTOS_SHARED_LIBRARY_VERSION:
return
_process_shared_library_info_8(self)
def _process_shared_library_info_8(photosdb: PhotosDB):
"""Process shared iCloud library info for Photos 8.0 and later
Args:
photosdb: an OSXPhotosDB instance
"""
db = photosdb._tmp_db
zasset = _DB_TABLE_NAMES[photosdb._photos_ver]["ASSET"]
(conn, cursor) = sqlite_open_ro(db)
result = cursor.execute(
f"""
SELECT
{zasset}.ZUUID,
{zasset}.ZACTIVELIBRARYSCOPEPARTICIPATIONSTATE,
{zasset}.ZLIBRARYSCOPESHARESTATE,
{zasset}.ZLIBRARYSCOPE
FROM {zasset}
"""
)
for row in result:
uuid = row[0]
if uuid not in photosdb._dbphotos:
logger.debug(f"Skipping shared library info for missing uuid: {uuid}")
continue
info = photosdb._dbphotos[uuid]
info["active_library_participation_state"] = row[1]
info["library_scope_share_state"] = row[2]
info["library_scope"] = row[3]

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from .._constants import _DB_TABLE_NAMES
from .._constants import _DB_TABLE_NAMES, _PHOTOS_SYNDICATION_MODEL_VERSION
from ..sqlite_utils import sqlite_open_ro
if TYPE_CHECKING:
@ -16,15 +16,18 @@ def _process_syndicationinfo(self: PhotosDB):
self._db_syndication_uuid = {}
if self.photos_version < 8:
if self.photos_version < 7:
raise NotImplementedError(
f"syndication info not implemented for this database version: {self.photos_version}"
)
else:
_process_syndicationinfo_8(self)
if self._model_ver < _PHOTOS_SYNDICATION_MODEL_VERSION:
return
_process_syndicationinfo_7(self)
def _process_syndicationinfo_8(photosdb: PhotosDB):
def _process_syndicationinfo_7(photosdb: PhotosDB):
"""Process Syndication info for Photos 8.0 and later
Args:

View File

@ -62,7 +62,11 @@ from ..rich_utils import add_rich_markup_tag
from ..sqlite_utils import sqlite_db_is_locked, sqlite_open_ro
from ..unicode import normalize_unicode
from ..utils import _check_file_exists, get_last_library_path, noop
from .photosdb_utils import get_db_model_version, get_db_version
from .photosdb_utils import (
get_db_version,
get_model_version,
get_photos_version_from_model,
)
if is_macos:
import photoscript
@ -91,6 +95,7 @@ class PhotosDB:
labels_normalized,
labels_normalized_as_dict,
)
from ._photosdb_process_shared_library import _process_shared_library_info
from ._photosdb_process_syndicationinfo import _process_syndicationinfo
def __init__(
@ -286,7 +291,7 @@ class PhotosDB:
# Dict to hold data on imports for Photos <= 4
self._db_import_group = {}
# Dict to hold syndication info for Photos >= 8
# Dict to hold syndication info for Photos >= 7
# key is UUID and value is dict of syndication info
self._db_syndication_uuid = {}
@ -326,7 +331,7 @@ class PhotosDB:
# photoanalysisd sometimes maintains this lock even after Photos is closed
# In those cases, make a temp copy of the file for sqlite3 to read
if sqlite_db_is_locked(self._dbfile):
verbose(f"Database locked, creating temporary copy.")
verbose("Database locked, creating temporary copy.")
self._tmp_db = self._copy_db_file(self._dbfile)
# _db_version is set from photos.db
@ -341,21 +346,23 @@ class PhotosDB:
self._photos_ver = 4
else:
self._photos_ver = 5
self._model_ver = 0 # only set for Photos 5+
# If Photos >= 5, actual data isn't in photos.db but in Photos.sqlite
if int(self._db_version) > int(_PHOTOS_4_VERSION):
dbpath = pathlib.Path(self._dbfile).parent
dbfile = dbpath / "Photos.sqlite"
if not _check_file_exists(dbfile):
raise FileNotFoundError(f"dbfile {dbfile} does not exist", dbfile)
else:
self._dbfile_actual = self._tmp_db = dbfile
verbose(f"Processing database {self._filepath(self._dbfile_actual)}")
# if database is exclusively locked, make a copy of it and use the copy
if sqlite_db_is_locked(self._dbfile_actual):
verbose(f"Database locked, creating temporary copy.")
self._tmp_db = self._copy_db_file(self._dbfile_actual)
self._dbfile_actual = self._tmp_db = dbfile
verbose(f"Processing database {self._filepath(self._dbfile_actual)}")
# if database is exclusively locked, make a copy of it and use the copy
if sqlite_db_is_locked(self._dbfile_actual):
verbose("Database locked, creating temporary copy.")
self._tmp_db = self._copy_db_file(self._dbfile_actual)
# set the photos version to actual value based on Photos.sqlite
self._photos_ver = get_db_model_version(self._tmp_db)
self._photos_ver = get_photos_version_from_model(self._tmp_db)
self._model_ver = get_model_version(self._tmp_db)
logger.debug(
f"_dbfile = {self._dbfile}, _dbfile_actual = {self._dbfile_actual}"
@ -1235,6 +1242,9 @@ class PhotosDB:
# photos 5+ only, for shared photos
self._dbphotos[uuid]["cloudownerhashedpersonid"] = None
# photos 7+ only, shared moments
self._dbphotos[uuid]["moment_share"] = None
# compute signatures for finding possible duplicates
signature = self._duplicate_signature(uuid)
try:
@ -1547,6 +1557,12 @@ class PhotosDB:
info["UTI_raw"] = None
info["raw_pair_info"] = None
# placeholders for shared library info on Photos 8+
for uuid in self._dbphotos:
self._dbphotos[uuid]["active_library_participation_state"] = None
self._dbphotos[uuid]["library_scope_share_state"] = None
self._dbphotos[uuid]["library_scope"] = None
# done with the database connection
conn.close()
@ -1706,6 +1722,7 @@ class PhotosDB:
# get info on keyface -- some photos have null keyface so can't do a single query
# (at least not with my SQL skills)
asset_fk = _DB_TABLE_NAMES[photos_ver]["DETECTED_FACE_ASSET_FK"]
c.execute(
f""" SELECT
ZPERSON.Z_PK,
@ -1714,7 +1731,7 @@ class PhotosDB:
ZDETECTEDFACE.ZUUID
FROM ZPERSON, ZDETECTEDFACE, {asset_table}
WHERE ZDETECTEDFACE.Z_PK = ZPERSON.ZKEYFACE AND
ZDETECTEDFACE.ZASSET = {asset_table}.Z_PK
{asset_fk} = {asset_table}.Z_PK
"""
)
@ -1733,13 +1750,14 @@ class PhotosDB:
# get information on detected faces
verbose("Processing detected faces in photos.")
person_fk = _DB_TABLE_NAMES[photos_ver]["DETECTED_FACE_PERSON_FK"]
c.execute(
f""" SELECT
ZPERSON.Z_PK,
{asset_table}.ZUUID
FROM ZPERSON, ZDETECTEDFACE, {asset_table}
WHERE ZDETECTEDFACE.ZPERSON = ZPERSON.Z_PK AND
ZDETECTEDFACE.ZASSET = {asset_table}.Z_PK
WHERE {person_fk} = ZPERSON.Z_PK AND
{asset_fk} = {asset_table}.Z_PK
"""
)
@ -1947,7 +1965,8 @@ class PhotosDB:
{asset_table}.ZSAVEDASSETTYPE,
{asset_table}.ZADDEDDATE,
{asset_table}.Z_PK,
{asset_table}.ZCLOUDOWNERHASHEDPERSONID
{asset_table}.ZCLOUDOWNERHASHEDPERSONID,
{asset_table}.ZMOMENTSHARE
FROM {asset_table}
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = {asset_table}.Z_PK
ORDER BY {asset_table}.ZUUID """
@ -1998,6 +2017,7 @@ class PhotosDB:
# 41 ZGENERICASSET.ZADDEDDATE -- date item added to the library
# 42 ZGENERICASSET.Z_PK -- primary key
# 43 ZGENERICASSET.ZCLOUDOWNERHASHEDPERSONID -- used to look up owner name (for shared photos)
# 44 ZASSET.ZMOMENTSHARE -- FK for ZSHARE (shared moments, Photos 5+; in Photos 7+ these are in the scopes/momentshared folder)
for row in c:
uuid = row[0]
@ -2185,6 +2205,8 @@ class PhotosDB:
info["pk"] = row[42]
info["cloudownerhashedpersonid"] = row[43]
info["moment_share"] = row[44]
# initialize import session info which will be filled in later
# not every photo has an import session so initialize all records now
info["import_session"] = None
@ -2209,6 +2231,11 @@ class PhotosDB:
info["UTI_edited_photo"] = None
info["UTI_edited_video"] = None
# placeholder for shared library info (Photos 8+)
info["active_library_participation_state"] = None
info["library_scope_share_state"] = None
info["library_scope"] = None
self._dbphotos[uuid] = info
# compute signatures for finding possible duplicates
@ -2517,10 +2544,14 @@ class PhotosDB:
verbose("Processing moments.")
self._process_moments()
if self.photos_version >= 8:
if self.photos_version >= 7:
verbose("Processing syndication info.")
self._process_syndicationinfo()
if self.photos_version >= 8:
verbose("Processing shared iCloud library info")
self._process_shared_library_info()
verbose("Done processing details from Photos library.")
def _process_moments(self):
@ -3530,6 +3561,16 @@ class PhotosDB:
elif options.not_saved_to_library:
photos = [p for p in photos if p.syndicated and not p.saved_to_library]
if options.shared_moment:
photos = [p for p in photos if p.shared_moment]
elif options.not_shared_moment:
photos = [p for p in photos if not p.shared_moment]
if options.shared_library:
photos = [p for p in photos if p.shared_library]
elif options.not_shared_library:
photos = [p for p in photos if not p.shared_library]
if options.function:
for function in options.function:
photos = function[0](photos)

View File

@ -16,6 +16,7 @@ from .._constants import (
_PHOTOS_6_MODEL_VERSION,
_PHOTOS_7_MODEL_VERSION,
_PHOTOS_8_MODEL_VERSION,
_PHOTOS_9_MODEL_VERSION,
_TESTED_DB_VERSIONS,
)
from ..sqlite_utils import sqlite_open_ro
@ -23,7 +24,7 @@ from ..sqlite_utils import sqlite_open_ro
__all__ = [
"get_db_version",
"get_model_version",
"get_db_model_version",
"get_photos_version_from_model",
"get_photos_library_version",
]
@ -93,7 +94,7 @@ def get_model_version(db_file: str) -> str:
return plist["PLModelVersion"]
def get_db_model_version(db_file: str) -> int:
def get_photos_version_from_model(db_file: str) -> int:
"""Returns Photos version based on model version found in db_file
Args:
@ -112,10 +113,12 @@ def get_db_model_version(db_file: str) -> int:
return 7
elif _PHOTOS_8_MODEL_VERSION[0] <= model_ver <= _PHOTOS_8_MODEL_VERSION[1]:
return 8
elif _PHOTOS_9_MODEL_VERSION[0] <= model_ver <= _PHOTOS_9_MODEL_VERSION[1]:
return 9
else:
logging.warning(f"Unknown model version: {model_ver}")
# cross our fingers and try latest version
return 8
return 9
def get_photos_library_version(library_path: str | pathlib.Path) -> int:
@ -149,10 +152,12 @@ def get_photos_library_version(library_path: str | pathlib.Path) -> int:
return 7
if _PHOTOS_8_MODEL_VERSION[0] <= model_ver <= _PHOTOS_8_MODEL_VERSION[1]:
return 8
if _PHOTOS_9_MODEL_VERSION[0] <= model_ver <= _PHOTOS_9_MODEL_VERSION[1]:
return 9
logging.warning(
f"Unknown db / model version: db_ver={db_ver}, model_ver={model_ver}; assuming Photos 8"
)
return 8
return 9
def get_db_path_for_library(photos_library: str | pathlib.Path) -> pathlib.Path:

View File

@ -204,10 +204,12 @@ class PersonTable(Table):
def rows(self) -> list[tuple[Any]]:
"""Return rows for this photo from the ZPERSON table."""
conn, cursor = self.db.get_db_connection()
person_fk = _DB_TABLE_NAMES[self.version]["DETECTED_FACE_PERSON_FK"]
asset_fk = _DB_TABLE_NAMES[self.version]["DETECTED_FACE_ASSET_FK"]
sql = f""" SELECT ZPERSON.*
FROM ZPERSON
JOIN ZDETECTEDFACE ON ZDETECTEDFACE.ZPERSON = ZPERSON.Z_PK
JOIN ZASSET ON ZASSET.Z_PK = ZDETECTEDFACE.ZASSET
JOIN ZDETECTEDFACE ON {person_fk} = ZPERSON.Z_PK
JOIN ZASSET ON ZASSET.Z_PK = {asset_fk}
WHERE {self.asset_table}.ZUUID = ?;
"""
cursor.execute(sql, (self.uuid,))
@ -216,10 +218,12 @@ class PersonTable(Table):
def _get_column(self, column: str) -> tuple[Any]:
"""Get column value for this photo from the ZPERSON table."""
conn, cursor = self.db.get_db_connection()
person_fk = _DB_TABLE_NAMES[self.version]["DETECTED_FACE_PERSON_FK"]
asset_fk = _DB_TABLE_NAMES[self.version]["DETECTED_FACE_ASSET_FK"]
sql = f""" SELECT ZPERSON.{column}
FROM ZPERSON
JOIN ZDETECTEDFACE ON ZDETECTEDFACE.ZPERSON = ZPERSON.Z_PK
JOIN ZASSET ON ZASSET.Z_PK = ZDETECTEDFACE.ZASSET
JOIN ZDETECTEDFACE ON {person_fk} = ZPERSON.Z_PK
JOIN ZASSET ON ZASSET.Z_PK = {asset_fk}
WHERE {self.asset_table}.ZUUID = ?;
"""
cursor.execute(sql, (self.uuid,))

View File

@ -130,9 +130,9 @@ e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{P
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
You can define variables for later use in the template string using the format `{var:NAME,VALUE}` where `VALUE` is a template statement. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
Variables can also be referenced as fields in the template string, for example: `{var:year,{created.year}}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:

View File

@ -149,9 +149,9 @@ e.g. `"{created.year}/{openbrace}{title}{closebrace}"` would result in `"2020/{P
**Variables**
You can define variables for later use in the template string using the format `{var:NAME,VALUE}`. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
You can define variables for later use in the template string using the format `{var:NAME,VALUE}` where `VALUE` is a template statement. Variables may then be referenced using the format `%NAME`. For example: `{var:foo,bar}` defines the variable `%foo` to have value `bar`. This can be useful if you want to re-use a complex template value in multiple places within your template string or for allowing the use of characters that would otherwise be prohibited in a template string. For example, the "pipe" (`|`) character is not allowed in a find/replace pair but you can get around this limitation like so: `{var:pipe,{pipe}}{title[-,%pipe]}` which replaces the `-` character with `|` (the value of `%pipe`).
Variables can also be referenced as fields in the template string, for example: `{var:year,created.year}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
Variables can also be referenced as fields in the template string, for example: `{var:year,{created.year}}{original_name}-{%year}`. In some cases, use of variables can make your template string more readable. Variables can be used as template fields, as values for filters, as values for conditional operations, or as default values. When used as a conditional value or default value, variables should be treated like any other field and enclosed in braces as conditional and default values are evaluated as template strings. For example: `{var:name,Katie}{person contains {%name}?{%name},Not-{%name}}`.
If you need to use a `%` (percent sign character), you can escape the percent sign by using `%%`. You can also use the `{percent}` template field where a template field is required. For example:

View File

@ -464,7 +464,7 @@ class PhotoTemplate:
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
"""
if type(template) is not str:
if not isinstance(template, str):
raise TypeError(f"template must be type str, not {type(template)}")
self.options = options
@ -1395,7 +1395,7 @@ class PhotoTemplate:
# if no uuid, then template is being validated but not actually run
# so don't run the function
values = []
elif caller in ["export", "query"]:
elif caller in {"export", "query"}:
# function signature is:
# def example(photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs) -> Union[List, str]:
values = template_func(self.photo, options=self.options, args=field_arg)
@ -1411,7 +1411,7 @@ class PhotoTemplate:
raise TypeError(
f"Invalid return type for function {funcname}: expected str or list"
)
if type(values) == str:
if isinstance(values, str):
values = [values]
# sanitize directory names if needed

View File

@ -112,6 +112,10 @@ class QueryOptions:
not_syndicated: search for photos that have not been shared via syndication ("Shared with You" album via Messages, etc.)
saved_to_library: search for syndicated photos that have been saved to the Photos library
not_saved_to_library: search for syndicated photos that have not been saved to the Photos library
shared_moment: search for photos that have been shared via a shared moment
not_shared_moment: search for photos that have not been shared via a shared moment
shared_library: search for photos that are part of a shared iCloud library
not_shared_library: search for photos that are not part of a shared iCloud library
"""
added_after: Optional[datetime.datetime] = None
@ -200,6 +204,10 @@ class QueryOptions:
not_syndicated: Optional[bool] = None
saved_to_library: Optional[bool] = None
not_saved_to_library: Optional[bool] = None
shared_moment: Optional[bool] = None
not_shared_moment: Optional[bool] = None
shared_library: Optional[bool] = None
not_shared_library: Optional[bool] = None
def asdict(self):
return asdict(self)
@ -271,7 +279,10 @@ def query_options_from_kwargs(**kwargs) -> QueryOptions:
("deleted_only", "not_deleted"),
("syndicated", "not_syndicated"),
("saved_to_library", "not_saved_to_library"),
("shared_moment", "not_shared_moment"),
("shared_library", "not_shared_library"),
]
# TODO: add option to validate requiring at least one query arg
for arg, not_arg in exclusive:
if kwargs.get(arg) and kwargs.get(not_arg):

200
osxphotos/shareinfo.py Normal file
View File

@ -0,0 +1,200 @@
"""Info about shared photos"""
from __future__ import annotations
import dataclasses
import datetime
from dataclasses import dataclass
from typing import TYPE_CHECKING
from ._constants import TIME_DELTA
if TYPE_CHECKING:
from .photosdb import PhotosDB
@dataclass
class ShareInfo:
"""Info about a share"""
_pk: int | None
cloud_delete_state: int | None
local_publish_state: int | None
public_permission: int | None
scope_type: int | None
status: int | None
trashed_state: int | None
auto_share_policy: int | None
cloud_item_count: int | None
cloud_local_state: int | None
cloud_photo_count: int | None
cloud_video_count: int | None
exit_state: int | None
participant_cloud_update_state: int | None
preview_state: int | None
scope_syncing_state: int | None
asset_count: int | None
force_sync_attempted: int | None
photos_count: int | None
should_ignore_budgets: int | None
should_notify_on_upload_completion: int | None
uploaded_photos_count: int | None
uploaded_videos_count: int | None
videos_count: int | None
creation_date: datetime.datetime | None
expiry_date: datetime.datetime | None
trashed_date: datetime.datetime | None
last_participant_asset_trash_notification_date: datetime.datetime | None
last_participant_asset_trash_notification_viewed_date: datetime.datetime | None
end_date: datetime.datetime | None
start_date: datetime.datetime | None
scope_identifier: str | None
title: str | None
uuid: str | None
originating_scope_identifier: str | None
share_url: str | None
rules_data: bytes | None
preview_data: bytes | None
thumbnail_image_data: bytes | None
exit_source: int | None
count_of_assets_added_by_camera_smart_sharing: int | None
exit_type: int | None
def __post_init__(self):
"""Convert dates from str to datetime"""
for field in [
"creation_date",
"expiry_date",
"trashed_date",
"last_participant_asset_trash_notification_date",
"last_participant_asset_trash_notification_viewed_date",
"end_date",
"start_date",
]:
if val := getattr(self, field):
setattr(self, field, datetime.datetime.fromtimestamp(val + TIME_DELTA))
def asdict(self):
"""Return info as dict"""
return dataclasses.asdict(self)
def get_moment_share_info(db: PhotosDB, uuid: str | None) -> ShareInfo:
"""Get info about a moment share"""
sql = """ SELECT
ZSHARE.Z_PK,
ZSHARE.ZCLOUDDELETESTATE,
ZSHARE.ZLOCALPUBLISHSTATE,
ZSHARE.ZPUBLICPERMISSION,
ZSHARE.ZSCOPETYPE,
ZSHARE.ZSTATUS,
ZSHARE.ZTRASHEDSTATE,
ZSHARE.ZAUTOSHAREPOLICY,
ZSHARE.ZCLOUDITEMCOUNT,
ZSHARE.ZCLOUDLOCALSTATE,
ZSHARE.ZCLOUDPHOTOCOUNT,
ZSHARE.ZCLOUDVIDEOCOUNT,
ZSHARE.ZEXITSTATE,
ZSHARE.ZPARTICIPANTCLOUDUPDATESTATE,
ZSHARE.ZPREVIEWSTATE,
ZSHARE.ZSCOPESYNCINGSTATE,
ZSHARE.ZASSETCOUNT,
ZSHARE.ZFORCESYNCATTEMPTED,
ZSHARE.ZPHOTOSCOUNT,
ZSHARE.ZSHOULDIGNOREBUDGETS,
ZSHARE.ZSHOULDNOTIFYONUPLOADCOMPLETION,
ZSHARE.ZUPLOADEDPHOTOSCOUNT,
ZSHARE.ZUPLOADEDVIDEOSCOUNT,
ZSHARE.ZVIDEOSCOUNT,
ZSHARE.ZCREATIONDATE,
ZSHARE.ZEXPIRYDATE,
ZSHARE.ZTRASHEDDATE,
ZSHARE.ZLASTPARTICIPANTASSETTRASHNOTIFICATIONDATE,
ZSHARE.ZLASTPARTICIPANTASSETTRASHNOTIFICATIONVIEWEDDATE,
ZSHARE.ZENDDATE,
ZSHARE.ZSTARTDATE,
ZSHARE.ZSCOPEIDENTIFIER,
ZSHARE.ZTITLE,
ZSHARE.ZUUID,
ZSHARE.ZORIGINATINGSCOPEIDENTIFIER,
ZSHARE.ZSHAREURL,
ZSHARE.ZRULESDATA,
ZSHARE.ZPREVIEWDATA,
ZSHARE.ZTHUMBNAILIMAGEDATA,
ZSHARE.ZEXITSOURCE,
ZSHARE.ZCOUNTOFASSETSADDEDBYCAMERASMARTSHARING,
ZSHARE.ZEXITTYPE
FROM ZSHARE
JOIN ZASSET ON ZASSET.ZMOMENTSHARE = ZSHARE.Z_PK
WHERE ZASSET.ZUUID = '{}'
;"""
sql = sql.format(uuid)
if row := db.execute(sql).fetchone():
return ShareInfo(*row)
raise ValueError(f"Could not find share for uuid {uuid}")
def get_share_info(db: PhotosDB, uuid: str | None) -> ShareInfo:
"""Get info about a moment share"""
# TODO: this is a total guess right now. I think that ZSHARE holds information
# about both shared moments and shared iCloud Library
# The foreign key for shared moments appears to be ZASSET.ZMOMENTSHARE
# but I don't know the key for shared iCloud Libraries
# I'm guessing it's ZASSET.ZSHARESCOPE but I don't know for sure and will need
# to test on a library that has shared iCloud Library and shared moments
sql = """ SELECT
ZSHARE.Z_PK,
ZSHARE.ZCLOUDDELETESTATE,
ZSHARE.ZLOCALPUBLISHSTATE,
ZSHARE.ZPUBLICPERMISSION,
ZSHARE.ZSCOPETYPE,
ZSHARE.ZSTATUS,
ZSHARE.ZTRASHEDSTATE,
ZSHARE.ZAUTOSHAREPOLICY,
ZSHARE.ZCLOUDITEMCOUNT,
ZSHARE.ZCLOUDLOCALSTATE,
ZSHARE.ZCLOUDPHOTOCOUNT,
ZSHARE.ZCLOUDVIDEOCOUNT,
ZSHARE.ZEXITSTATE,
ZSHARE.ZPARTICIPANTCLOUDUPDATESTATE,
ZSHARE.ZPREVIEWSTATE,
ZSHARE.ZSCOPESYNCINGSTATE,
ZSHARE.ZASSETCOUNT,
ZSHARE.ZFORCESYNCATTEMPTED,
ZSHARE.ZPHOTOSCOUNT,
ZSHARE.ZSHOULDIGNOREBUDGETS,
ZSHARE.ZSHOULDNOTIFYONUPLOADCOMPLETION,
ZSHARE.ZUPLOADEDPHOTOSCOUNT,
ZSHARE.ZUPLOADEDVIDEOSCOUNT,
ZSHARE.ZVIDEOSCOUNT,
ZSHARE.ZCREATIONDATE,
ZSHARE.ZEXPIRYDATE,
ZSHARE.ZTRASHEDDATE,
ZSHARE.ZLASTPARTICIPANTASSETTRASHNOTIFICATIONDATE,
ZSHARE.ZLASTPARTICIPANTASSETTRASHNOTIFICATIONVIEWEDDATE,
ZSHARE.ZENDDATE,
ZSHARE.ZSTARTDATE,
ZSHARE.ZSCOPEIDENTIFIER,
ZSHARE.ZTITLE,
ZSHARE.ZUUID,
ZSHARE.ZORIGINATINGSCOPEIDENTIFIER,
ZSHARE.ZSHAREURL,
ZSHARE.ZRULESDATA,
ZSHARE.ZPREVIEWDATA,
ZSHARE.ZTHUMBNAILIMAGEDATA,
ZSHARE.ZEXITSOURCE,
ZSHARE.ZCOUNTOFASSETSADDEDBYCAMERASMARTSHARING,
ZSHARE.ZEXITTYPE
FROM ZSHARE
JOIN ZASSET ON ZASSET.ZLIBRARYSCOPE = ZSHARE.Z_PK
WHERE ZASSET.ZUUID = '{}'
;"""
sql = sql.format(uuid)
if row := db.execute(sql).fetchone():
return ShareInfo(*row)
raise ValueError(f"Could not find share for uuid {uuid}")

View File

@ -0,0 +1,63 @@
"""Information about share participants for shared photos"""
from __future__ import annotations
import dataclasses
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .photosdb import PhotosDB
def get_share_participants(db: PhotosDB, uuid: str) -> list[ShareParticipant]:
"""Return list of ShareParticipant objects for the given database"""
sql = """ SELECT
ZSHAREPARTICIPANT.Z_PK,
ZSHAREPARTICIPANT.ZACCEPTANCESTATUS,
ZSHAREPARTICIPANT.ZISCURRENTUSER,
ZSHAREPARTICIPANT.ZEXITSTATE,
ZSHAREPARTICIPANT.ZPERMISSION,
ZSHAREPARTICIPANT.ZPERSON,
ZSHAREPARTICIPANT.Z54_SHARE,
ZSHAREPARTICIPANT.ZSHARE,
ZSHAREPARTICIPANT.ZEMAILADDRESS,
ZSHAREPARTICIPANT.ZPARTICIPANTID,
ZSHAREPARTICIPANT.ZPHONENUMBER,
ZSHAREPARTICIPANT.ZUSERIDENTIFIER,
ZSHAREPARTICIPANT.ZUUID,
ZSHAREPARTICIPANT.ZNAMECOMPONENTS
FROM ZSHAREPARTICIPANT
JOIN ZASSETCONTRIBUTOR ON ZSHAREPARTICIPANT.Z_PK = ZASSETCONTRIBUTOR.ZPARTICIPANT
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.Z_PK = ZASSETCONTRIBUTOR.Z3LIBRARYSCOPEASSETCONTRIBUTORS
JOIN ZASSET ON ZASSET.Z_PK = ZADDITIONALASSETATTRIBUTES.ZASSET
WHERE ZASSET.ZUUID = '{}';""".format(
uuid
)
rows = db.execute(sql)
return [ShareParticipant(*row) for row in rows]
@dataclass
class ShareParticipant:
"""Information about a share participant"""
_pk: int
_acceptance_status: int
is_current_user: bool
_exit_state: int
_permission: int
_person: int
_z54_share: int
_share: int
email_address: str
participant_id: str
phone_number: str
user_identifier: str
uuid: str
_name_components: bytes
def asdict(self):
"""Return share participant as a dict"""
return dataclasses.asdict(self)

View File

@ -40,7 +40,7 @@ def sqlgrep(
for row_num, row in enumerate(cursor):
for field in row.keys():
field_value = row[field]
if not field_value or type(field_value) == bytes:
if not field_value or isinstance(field_value, bytes):
# don't search binary blobs
next
field_value = str(field_value)

View File

@ -253,8 +253,6 @@ def list_photo_libraries():
return lib_list
# def findfiles(pattern, path):
# """Returns list of filenames from path matched by pattern
# shell pattern. Matching is case-insensitive.

18
pyproject.toml Normal file
View File

@ -0,0 +1,18 @@
[tool.ruff]
# Docs: https://beta.ruff.rs/docs/
# Rules: https://beta.ruff.rs/docs/rules/
target-version = "py39"
line-length = 366
ignore = ["E402","E722","E741","F401","F403","F405","F541","F822","F841"]
# Exclude a variety of commonly ignored directories.
exclude = [
".git",
".mypy_cache",
".pre-commit-cache",
".ruff_cache",
".tox",
".venv",
"venv",
"docs",
"__pycache",
]

12
sweep.yaml Normal file
View File

@ -0,0 +1,12 @@
# Sweep AI turns bug fixes & feature requests into code changes (https://sweep.dev)
# For details on our config file, check out our docs at https://docs.sweep.dev
# If you use this be sure to frequently sync your default branch(main, master) to dev.
branch: 'main'
# By default Sweep will read the logs and outputs from your existing Github Actions. To disable this, set this to false.
gha_enabled: True
# This is the description of your project. It will be used by sweep when creating PRs. You can tell Sweep what's unique about your project, what frameworks you use, or anything else you want.
# Here's an example: sweepai/sweep is a python project. The main api endpoints are in sweepai/api.py. Write code that adheres to PEP8.
description: ''
# Default Values: https://github.com/sweepai/sweep/blob/main/sweep.yaml

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LibrarySchemaVersion</key>
<integer>5001</integer>
<key>MetaSchemaVersion</key>
<integer>3</integer>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>hostname</key>
<string>rhets-Virtual-Machine.local</string>
<key>hostuuid</key>
<string>DB248ADD-817E-5469-8229-D55158E1D479</string>
<key>pid</key>
<integer>627</integer>
<key>processname</key>
<string>photolibraryd</string>
<key>uid</key>
<integer>501</integer>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More