diff --git a/README.md b/README.md
index d57563ab..8287f7f9 100644
--- a/README.md
+++ b/README.md
@@ -124,9 +124,10 @@ This package will install a command line utility called `osxphotos` that allows
```
> osxphotos
-
Usage: osxphotos [OPTIONS] COMMAND [ARGS]...
+ osxphotos: query and export your Photos library
+
Options:
--db Specify Photos database path. Path to Photos
library/database can be specified using either
@@ -144,8 +145,8 @@ 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
- dump Print list of all photos & associated info from the Photos...
docs Open osxphotos documentation in your browser.
+ dump Print list of all photos & associated info from the Photos...
export Export photos from the Photos database.
help Print help; for help on commands: help .
info Print out descriptive info of the Photos library database.
@@ -157,7 +158,10 @@ Commands:
places Print out places found in the Photos library.
query Query the Photos database using 1 or more search options; if...
repl Run interactive osxphotos REPL shell (useful for debugging,...
+ run Run a python file using same environment as osxphotos
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
diff --git a/README.rst b/README.rst
index ab41243c..6fdc513f 100644
--- a/README.rst
+++ b/README.rst
@@ -95,6 +95,7 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
-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.
@@ -115,6 +116,7 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
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
diff --git a/docs/_sources/overview.rst.txt b/docs/_sources/overview.rst.txt
index e4f6b9a3..20c2c14b 100644
--- a/docs/_sources/overview.rst.txt
+++ b/docs/_sources/overview.rst.txt
@@ -74,6 +74,7 @@ This package will install a command line utility called ``osxphotos`` that allow
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
diff --git a/docs/cli.html b/docs/cli.html
index bc9535c8..dab405f9 100644
--- a/docs/cli.html
+++ b/docs/cli.html
@@ -2110,6 +2110,118 @@ uses ‘/private/tmp/osxphotos_snapshots’
tutorial
Display osxphotos tutorial.
@@ -2289,6 +2401,7 @@ Commands:
run
snap
theme
+timewarp
tutorial
uninstall
uuid
diff --git a/docs/genindex.html b/docs/genindex.html
index 0dc56517..6af34048 100644
--- a/docs/genindex.html
+++ b/docs/genindex.html
@@ -227,6 +227,8 @@
@@ -279,6 +281,13 @@
osxphotos-query command line option
osxphotos-repl command line option
+
+
+
+ --compare-exif
+
+
@@ -307,6 +316,20 @@
+
+ --date
+
+
+
+ --date-delta
+
+
@@ -506,6 +529,8 @@
@@ -712,6 +737,13 @@
osxphotos-query command line option
osxphotos-repl command line option
+
+
+
+ --inspect
+
+
@@ -791,6 +823,13 @@
osxphotos-query command line option
osxphotos-repl command line option
+
+
+
+ --library
+
+
@@ -827,6 +866,13 @@
osxphotos-query command line option
osxphotos-repl command line option
+
+
+
+ --match-time
+
+
@@ -946,8 +992,6 @@
osxphotos-repl command line option
-
-
+ |
+
+
+ --plain
+
+
@@ -1247,6 +1307,20 @@
+
+ --pull-exif
+
+
+
+ --push-exif
+
+
@@ -1468,6 +1542,22 @@
+
+ --time
+
+
+
+ --time-delta
+
+
@@ -1486,6 +1576,15 @@
+
+ --timezone
+
+
@@ -1547,6 +1646,13 @@
+
+ --use-file-time
+
+
@@ -1603,6 +1709,8 @@
osxphotos-diff command line option
osxphotos-export command line option
+
+ osxphotos-timewarp command line option
@@ -1635,12 +1743,49 @@
+
+ -a
+
+
+
+ -c
+
+
+
+ -D
+
+
+
+ -d
+
+
+
+ -e
+
+
-f
@@ -1653,6 +1798,43 @@
osxphotos-query command line option
osxphotos-repl command line option
+
+ osxphotos-timewarp command line option
+
+
+
+ -L
+
+
+
+ -m
+
+
+
+ -o
+
+
+
+ -P
+
+
+
+ -p
+
+
@@ -1667,6 +1849,20 @@
+
+ -T
+
+
+
+ -t
+
+
@@ -1683,6 +1879,8 @@
osxphotos-diff command line option
osxphotos-export command line option
+
+ osxphotos-timewarp command line option
@@ -1697,6 +1895,13 @@
+
+ -z
+
+
|
@@ -2760,8 +2965,6 @@
--json
-
-
-
osxphotos-persons command line option
@@ -2949,6 +3152,8 @@
- PHOTOS_LIBRARY
+ |
+
-
osxphotos-repl command line option
@@ -3141,6 +3346,81 @@
- --list
- --preview
+
+
+
+ osxphotos-timewarp command line option
+
+
diff --git a/docs/index.html b/docs/index.html
index 7bb51203..acd056e0 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -251,6 +251,7 @@
run
snap
theme
+timewarp
tutorial
uninstall
uuid
diff --git a/docs/objects.inv b/docs/objects.inv
index 0019bfef..24fb93c6 100644
Binary files a/docs/objects.inv and b/docs/objects.inv differ
diff --git a/docs/overview.html b/docs/overview.html
index c7f81a3f..99d76a22 100644
--- a/docs/overview.html
+++ b/docs/overview.html
@@ -260,6 +260,7 @@ E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine
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
diff --git a/docs/searchindex.js b/docs/searchindex.js
index fda98ee9..661a75b0 100644
--- a/docs/searchindex.js
+++ b/docs/searchindex.js
@@ -1 +1 @@
-Search.setIndex({docnames:["cli","index","overview","package_overview","reference","template_help","tutorial"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["cli.rst","index.rst","overview.rst","package_overview.rst","reference.rst","template_help.rst","tutorial.rst"],objects:{"":[[4,0,0,"-","osxphotos"]],"osxphotos-albums":[[0,6,1,"cmdoption-osxphotos-albums-db","--db"],[0,6,1,"cmdoption-osxphotos-albums-json","--json"],[0,6,1,"cmdoption-osxphotos-albums-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-diff":[[0,6,1,"cmdoption-osxphotos-diff-db","--db"],[0,6,1,"cmdoption-osxphotos-diff-r","--raw-output"],[0,6,1,"cmdoption-osxphotos-diff-s","--style"],[0,6,1,"cmdoption-osxphotos-diff-V","--verbose"],[0,6,1,"cmdoption-osxphotos-diff-V","-V"],[0,6,1,"cmdoption-osxphotos-diff-r","-r"],[0,6,1,"cmdoption-osxphotos-diff-s","-s"],[0,6,1,"cmdoption-osxphotos-diff-arg-DB2","DB2"]],"osxphotos-dump":[[0,6,1,"cmdoption-osxphotos-dump-db","--db"],[0,6,1,"cmdoption-osxphotos-dump-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-dump-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-dump-json","--json"],[0,6,1,"cmdoption-osxphotos-dump-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-export":[[0,6,1,"cmdoption-osxphotos-export-add-exported-to-album","--add-exported-to-album"],[0,6,1,"cmdoption-osxphotos-export-add-missing-to-album","--add-missing-to-album"],[0,6,1,"cmdoption-osxphotos-export-add-skipped-to-album","--add-skipped-to-album"],[0,6,1,"cmdoption-osxphotos-export-album","--album"],[0,6,1,"cmdoption-osxphotos-export-album-keyword","--album-keyword"],[0,6,1,"cmdoption-osxphotos-export-burst","--burst"],[0,6,1,"cmdoption-osxphotos-export-cleanup","--cleanup"],[0,6,1,"cmdoption-osxphotos-export-config-only","--config-only"],[0,6,1,"cmdoption-osxphotos-export-convert-to-jpeg","--convert-to-jpeg"],[0,6,1,"cmdoption-osxphotos-export-current-name","--current-name"],[0,6,1,"cmdoption-osxphotos-export-db","--db"],[0,6,1,"cmdoption-osxphotos-export-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-export-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-export-description","--description"],[0,6,1,"cmdoption-osxphotos-export-description-template","--description-template"],[0,6,1,"cmdoption-osxphotos-export-directory","--directory"],[0,6,1,"cmdoption-osxphotos-export-download-missing","--download-missing"],[0,6,1,"cmdoption-osxphotos-export-dry-run","--dry-run"],[0,6,1,"cmdoption-osxphotos-export-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-export-edited","--edited"],[0,6,1,"cmdoption-osxphotos-export-edited-suffix","--edited-suffix"],[0,6,1,"cmdoption-osxphotos-export-exif","--exif"],[0,6,1,"cmdoption-osxphotos-export-exiftool","--exiftool"],[0,6,1,"cmdoption-osxphotos-export-exiftool-merge-keywords","--exiftool-merge-keywords"],[0,6,1,"cmdoption-osxphotos-export-exiftool-merge-persons","--exiftool-merge-persons"],[0,6,1,"cmdoption-osxphotos-export-exiftool-option","--exiftool-option"],[0,6,1,"cmdoption-osxphotos-export-exiftool-path","--exiftool-path"],[0,6,1,"cmdoption-osxphotos-export-export-as-hardlink","--export-as-hardlink"],[0,6,1,"cmdoption-osxphotos-export-export-by-date","--export-by-date"],[0,6,1,"cmdoption-osxphotos-export-exportdb","--exportdb"],[0,6,1,"cmdoption-osxphotos-export-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-export-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-export-filename","--filename"],[0,6,1,"cmdoption-osxphotos-export-finder-tag-keywords","--finder-tag-keywords"],[0,6,1,"cmdoption-osxphotos-export-finder-tag-template","--finder-tag-template"],[0,6,1,"cmdoption-osxphotos-export-folder","--folder"],[0,6,1,"cmdoption-osxphotos-export-force-update","--force-update"],[0,6,1,"cmdoption-osxphotos-export-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-export-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-export-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-export-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-export-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-export-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-export-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-export-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-export-ignore-date-modified","--ignore-date-modified"],[0,6,1,"cmdoption-osxphotos-export-ignore-signature","--ignore-signature"],[0,6,1,"cmdoption-osxphotos-export-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-export-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-export-jpeg-ext","--jpeg-ext"],[0,6,1,"cmdoption-osxphotos-export-jpeg-quality","--jpeg-quality"],[0,6,1,"cmdoption-osxphotos-export-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-export-keyword-template","--keyword-template"],[0,6,1,"cmdoption-osxphotos-export-label","--label"],[0,6,1,"cmdoption-osxphotos-export-live","--live"],[0,6,1,"cmdoption-osxphotos-export-load-config","--load-config"],[0,6,1,"cmdoption-osxphotos-export-location","--location"],[0,6,1,"cmdoption-osxphotos-export-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-export-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-export-missing","--missing"],[0,6,1,"cmdoption-osxphotos-export-name","--name"],[0,6,1,"cmdoption-osxphotos-export-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-export-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-export-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-export-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-export-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-export-no-progress","--no-progress"],[0,6,1,"cmdoption-osxphotos-export-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-export-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-export-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-export-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-export-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-export-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-export-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-export-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-export-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-export-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-export-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-export-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-export-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-export-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-export-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-export-only-new","--only-new"],[0,6,1,"cmdoption-osxphotos-export-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-export-original-suffix","--original-suffix"],[0,6,1,"cmdoption-osxphotos-export-overwrite","--overwrite"],[0,6,1,"cmdoption-osxphotos-export-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-export-person","--person"],[0,6,1,"cmdoption-osxphotos-export-person-keyword","--person-keyword"],[0,6,1,"cmdoption-osxphotos-export-place","--place"],[0,6,1,"cmdoption-osxphotos-export-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-export-post-command","--post-command"],[0,6,1,"cmdoption-osxphotos-export-post-function","--post-function"],[0,6,1,"cmdoption-osxphotos-export-preview","--preview"],[0,6,1,"cmdoption-osxphotos-export-preview-if-missing","--preview-if-missing"],[0,6,1,"cmdoption-osxphotos-export-preview-suffix","--preview-suffix"],[0,6,1,"cmdoption-osxphotos-export-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-export-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-export-ramdb","--ramdb"],[0,6,1,"cmdoption-osxphotos-export-regex","--regex"],[0,6,1,"cmdoption-osxphotos-export-replace-keywords","--replace-keywords"],[0,6,1,"cmdoption-osxphotos-export-report","--report"],[0,6,1,"cmdoption-osxphotos-export-retry","--retry"],[0,6,1,"cmdoption-osxphotos-export-save-config","--save-config"],[0,6,1,"cmdoption-osxphotos-export-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-export-selected","--selected"],[0,6,1,"cmdoption-osxphotos-export-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-export-shared","--shared"],[0,6,1,"cmdoption-osxphotos-export-sidecar","--sidecar"],[0,6,1,"cmdoption-osxphotos-export-sidecar-drop-ext","--sidecar-drop-ext"],[0,6,1,"cmdoption-osxphotos-export-skip-bursts","--skip-bursts"],[0,6,1,"cmdoption-osxphotos-export-skip-edited","--skip-edited"],[0,6,1,"cmdoption-osxphotos-export-skip-live","--skip-live"],[0,6,1,"cmdoption-osxphotos-export-skip-original-if-edited","--skip-original-if-edited"],[0,6,1,"cmdoption-osxphotos-export-skip-raw","--skip-raw"],[0,6,1,"cmdoption-osxphotos-export-skip-uuid","--skip-uuid"],[0,6,1,"cmdoption-osxphotos-export-skip-uuid-from-file","--skip-uuid-from-file"],[0,6,1,"cmdoption-osxphotos-export-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-export-strip","--strip"],[0,6,1,"cmdoption-osxphotos-export-theme","--theme"],[0,6,1,"cmdoption-osxphotos-export-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-export-timestamp","--timestamp"],[0,6,1,"cmdoption-osxphotos-export-title","--title"],[0,6,1,"cmdoption-osxphotos-export-tmpdir","--tmpdir"],[0,6,1,"cmdoption-osxphotos-export-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-export-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-export-touch-file","--touch-file"],[0,6,1,"cmdoption-osxphotos-export-update","--update"],[0,6,1,"cmdoption-osxphotos-export-use-photokit","--use-photokit"],[0,6,1,"cmdoption-osxphotos-export-use-photos-export","--use-photos-export"],[0,6,1,"cmdoption-osxphotos-export-uti","--uti"],[0,6,1,"cmdoption-osxphotos-export-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-export-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-export-V","--verbose"],[0,6,1,"cmdoption-osxphotos-export-xattr-template","--xattr-template"],[0,6,1,"cmdoption-osxphotos-export-year","--year"],[0,6,1,"cmdoption-osxphotos-export-V","-V"],[0,6,1,"cmdoption-osxphotos-export-i","-i"],[0,6,1,"cmdoption-osxphotos-export-arg-DEST","DEST"],[0,6,1,"cmdoption-osxphotos-export-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-help":[[0,6,1,"cmdoption-osxphotos-help-arg-SUBTOPIC","SUBTOPIC"],[0,6,1,"cmdoption-osxphotos-help-arg-TOPIC","TOPIC"]],"osxphotos-info":[[0,6,1,"cmdoption-osxphotos-info-db","--db"],[0,6,1,"cmdoption-osxphotos-info-json","--json"],[0,6,1,"cmdoption-osxphotos-info-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-install":[[0,6,1,"cmdoption-osxphotos-install-U","--upgrade"],[0,6,1,"cmdoption-osxphotos-install-U","-U"],[0,6,1,"cmdoption-osxphotos-install-arg-PACKAGES","PACKAGES"]],"osxphotos-keywords":[[0,6,1,"cmdoption-osxphotos-keywords-db","--db"],[0,6,1,"cmdoption-osxphotos-keywords-json","--json"],[0,6,1,"cmdoption-osxphotos-keywords-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-labels":[[0,6,1,"cmdoption-osxphotos-labels-db","--db"],[0,6,1,"cmdoption-osxphotos-labels-json","--json"],[0,6,1,"cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-list":[[0,6,1,"cmdoption-osxphotos-list-json","--json"]],"osxphotos-persons":[[0,6,1,"cmdoption-osxphotos-persons-db","--db"],[0,6,1,"cmdoption-osxphotos-persons-json","--json"],[0,6,1,"cmdoption-osxphotos-persons-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-places":[[0,6,1,"cmdoption-osxphotos-places-db","--db"],[0,6,1,"cmdoption-osxphotos-places-json","--json"],[0,6,1,"cmdoption-osxphotos-places-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-query":[[0,6,1,"cmdoption-osxphotos-query-add-to-album","--add-to-album"],[0,6,1,"cmdoption-osxphotos-query-album","--album"],[0,6,1,"cmdoption-osxphotos-query-burst","--burst"],[0,6,1,"cmdoption-osxphotos-query-cloudasset","--cloudasset"],[0,6,1,"cmdoption-osxphotos-query-db","--db"],[0,6,1,"cmdoption-osxphotos-query-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-query-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-query-description","--description"],[0,6,1,"cmdoption-osxphotos-query-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-query-edited","--edited"],[0,6,1,"cmdoption-osxphotos-query-exif","--exif"],[0,6,1,"cmdoption-osxphotos-query-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-query-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-query-folder","--folder"],[0,6,1,"cmdoption-osxphotos-query-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-query-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-query-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-query-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-query-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-query-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-query-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-query-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-query-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-query-incloud","--incloud"],[0,6,1,"cmdoption-osxphotos-query-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-query-json","--json"],[0,6,1,"cmdoption-osxphotos-query-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-query-label","--label"],[0,6,1,"cmdoption-osxphotos-query-live","--live"],[0,6,1,"cmdoption-osxphotos-query-location","--location"],[0,6,1,"cmdoption-osxphotos-query-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-query-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-query-missing","--missing"],[0,6,1,"cmdoption-osxphotos-query-name","--name"],[0,6,1,"cmdoption-osxphotos-query-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-query-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-query-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-query-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-query-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-query-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-query-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-query-not-cloudasset","--not-cloudasset"],[0,6,1,"cmdoption-osxphotos-query-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-query-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-query-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-query-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-query-not-incloud","--not-incloud"],[0,6,1,"cmdoption-osxphotos-query-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-query-not-missing","--not-missing"],[0,6,1,"cmdoption-osxphotos-query-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-query-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-query-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-query-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-query-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-query-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-query-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-query-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-query-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-query-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-query-person","--person"],[0,6,1,"cmdoption-osxphotos-query-place","--place"],[0,6,1,"cmdoption-osxphotos-query-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-query-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-query-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-query-regex","--regex"],[0,6,1,"cmdoption-osxphotos-query-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-query-selected","--selected"],[0,6,1,"cmdoption-osxphotos-query-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-query-shared","--shared"],[0,6,1,"cmdoption-osxphotos-query-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-query-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-query-title","--title"],[0,6,1,"cmdoption-osxphotos-query-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-query-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-query-uti","--uti"],[0,6,1,"cmdoption-osxphotos-query-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-query-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-query-year","--year"],[0,6,1,"cmdoption-osxphotos-query-i","-i"],[0,6,1,"cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-repl":[[0,6,1,"cmdoption-osxphotos-repl-album","--album"],[0,6,1,"cmdoption-osxphotos-repl-burst","--burst"],[0,6,1,"cmdoption-osxphotos-repl-cloudasset","--cloudasset"],[0,6,1,"cmdoption-osxphotos-repl-db","--db"],[0,6,1,"cmdoption-osxphotos-repl-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-repl-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-repl-description","--description"],[0,6,1,"cmdoption-osxphotos-repl-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-repl-edited","--edited"],[0,6,1,"cmdoption-osxphotos-repl-emacs","--emacs"],[0,6,1,"cmdoption-osxphotos-repl-exif","--exif"],[0,6,1,"cmdoption-osxphotos-repl-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-repl-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-repl-folder","--folder"],[0,6,1,"cmdoption-osxphotos-repl-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-repl-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-repl-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-repl-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-repl-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-repl-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-repl-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-repl-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-repl-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-repl-incloud","--incloud"],[0,6,1,"cmdoption-osxphotos-repl-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-repl-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-repl-label","--label"],[0,6,1,"cmdoption-osxphotos-repl-live","--live"],[0,6,1,"cmdoption-osxphotos-repl-location","--location"],[0,6,1,"cmdoption-osxphotos-repl-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-repl-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-repl-missing","--missing"],[0,6,1,"cmdoption-osxphotos-repl-name","--name"],[0,6,1,"cmdoption-osxphotos-repl-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-repl-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-repl-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-repl-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-repl-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-repl-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-repl-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-repl-not-cloudasset","--not-cloudasset"],[0,6,1,"cmdoption-osxphotos-repl-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-repl-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-repl-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-repl-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-repl-not-incloud","--not-incloud"],[0,6,1,"cmdoption-osxphotos-repl-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-repl-not-missing","--not-missing"],[0,6,1,"cmdoption-osxphotos-repl-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-repl-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-repl-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-repl-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-repl-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-repl-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-repl-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-repl-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-repl-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-repl-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-repl-person","--person"],[0,6,1,"cmdoption-osxphotos-repl-place","--place"],[0,6,1,"cmdoption-osxphotos-repl-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-repl-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-repl-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-repl-regex","--regex"],[0,6,1,"cmdoption-osxphotos-repl-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-repl-selected","--selected"],[0,6,1,"cmdoption-osxphotos-repl-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-repl-shared","--shared"],[0,6,1,"cmdoption-osxphotos-repl-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-repl-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-repl-title","--title"],[0,6,1,"cmdoption-osxphotos-repl-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-repl-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-repl-uti","--uti"],[0,6,1,"cmdoption-osxphotos-repl-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-repl-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-repl-year","--year"],[0,6,1,"cmdoption-osxphotos-repl-i","-i"]],"osxphotos-run":[[0,6,1,"cmdoption-osxphotos-run-arg-PYTHON_FILE","PYTHON_FILE"]],"osxphotos-snap":[[0,6,1,"cmdoption-osxphotos-snap-db","--db"]],"osxphotos-theme":[[0,6,1,"cmdoption-osxphotos-theme-clone","--clone"],[0,6,1,"cmdoption-osxphotos-theme-config","--config"],[0,6,1,"cmdoption-osxphotos-theme-default","--default"],[0,6,1,"cmdoption-osxphotos-theme-delete","--delete"],[0,6,1,"cmdoption-osxphotos-theme-edit","--edit"],[0,6,1,"cmdoption-osxphotos-theme-list","--list"],[0,6,1,"cmdoption-osxphotos-theme-preview","--preview"]],"osxphotos-tutorial":[[0,6,1,"cmdoption-osxphotos-tutorial-arg-WIDTH","WIDTH"]],"osxphotos-uninstall":[[0,6,1,"cmdoption-osxphotos-uninstall-y","--yes"],[0,6,1,"cmdoption-osxphotos-uninstall-y","-y"],[0,6,1,"cmdoption-osxphotos-uninstall-arg-PACKAGES","PACKAGES"]],"osxphotos-uuid":[[0,6,1,"cmdoption-osxphotos-uuid-f","--filename"],[0,6,1,"cmdoption-osxphotos-uuid-f","-f"]],"osxphotos-version":[[0,6,1,"cmdoption-osxphotos-version-run","--run"]],"osxphotos.AlbumInfo":[[4,2,1,"","folder_list"],[4,2,1,"","folder_names"],[4,2,1,"","parent"],[4,3,1,"","photo_index"],[4,2,1,"","photos"],[4,2,1,"","sort_order"],[4,2,1,"","title"]],"osxphotos.ExifTool":[[4,3,1,"","addvalues"],[4,3,1,"","asdict"],[4,3,1,"","json"],[4,2,1,"","pid"],[4,3,1,"","run_commands"],[4,3,1,"","setvalue"],[4,2,1,"","version"]],"osxphotos.ExportDB":[[4,3,1,"","close"],[4,3,1,"","create_file_record"],[4,3,1,"","create_or_get_file_record"],[4,2,1,"","export_dir"],[4,3,1,"","get_file_record"],[4,3,1,"","get_photoinfo_for_uuid"],[4,3,1,"","get_previous_uuids"],[4,3,1,"","get_uuid_for_file"],[4,2,1,"","path"],[4,3,1,"","set_config"],[4,3,1,"","set_photoinfo_for_uuid"]],"osxphotos.ExportOptions":[[4,2,1,"","bit_flags"],[4,4,1,"","convert_to_jpeg"],[4,4,1,"","description_template"],[4,4,1,"","download_missing"],[4,4,1,"","dry_run"],[4,4,1,"","edited"],[4,4,1,"","exiftool"],[4,4,1,"","exiftool_flags"],[4,4,1,"","export_as_hardlink"],[4,4,1,"","export_db"],[4,4,1,"","face_regions"],[4,4,1,"","fileutil"],[4,4,1,"","force_update"],[4,4,1,"","ignore_date_modified"],[4,4,1,"","ignore_signature"],[4,4,1,"","increment"],[4,4,1,"","jpeg_ext"],[4,4,1,"","jpeg_quality"],[4,4,1,"","keyword_template"],[4,4,1,"","live_photo"],[4,4,1,"","location"],[4,4,1,"","merge_exif_keywords"],[4,4,1,"","merge_exif_persons"],[4,4,1,"","overwrite"],[4,4,1,"","persons"],[4,4,1,"","preview"],[4,4,1,"","preview_suffix"],[4,4,1,"","raw_photo"],[4,4,1,"","render_options"],[4,4,1,"","replace_keywords"],[4,4,1,"","rich"],[4,4,1,"","sidecar"],[4,4,1,"","sidecar_drop_ext"],[4,4,1,"","strip"],[4,4,1,"","timeout"],[4,4,1,"","tmpdir"],[4,4,1,"","touch_file"],[4,4,1,"","update"],[4,4,1,"","use_albums_as_keywords"],[4,4,1,"","use_persons_as_keywords"],[4,4,1,"","use_photokit"],[4,4,1,"","use_photos_export"],[4,4,1,"","verbose"]],"osxphotos.ExportResults":[[4,3,1,"","all_files"]],"osxphotos.FileUtilNoOp":[[4,3,1,"","convert_to_jpeg"],[4,3,1,"","copy"],[4,3,1,"","file_sig"],[4,3,1,"","hardlink"],[4,3,1,"","rename"],[4,3,1,"","rmdir"],[4,3,1,"","tmpdir"],[4,3,1,"","unlink"],[4,3,1,"","utime"]],"osxphotos.FolderInfo":[[4,2,1,"","album_info"],[4,2,1,"","parent"],[4,2,1,"","subfolders"],[4,2,1,"","title"],[4,2,1,"","uuid"]],"osxphotos.ImportInfo":[[4,2,1,"","photos"]],"osxphotos.MomentInfo":[[4,2,1,"","date"],[4,2,1,"","end_date"],[4,2,1,"","location"],[4,2,1,"","modification_date"],[4,2,1,"","photos"],[4,2,1,"","pk"],[4,2,1,"","start_date"],[4,2,1,"","subtitle"],[4,2,1,"","title"]],"osxphotos.PersonInfo":[[4,3,1,"","asdict"],[4,2,1,"","face_info"],[4,3,1,"","json"],[4,2,1,"","photos"]],"osxphotos.PhotoExporter":[[4,3,1,"","export"]],"osxphotos.PhotoInfo":[[4,2,1,"","adjustments"],[4,2,1,"","album_info"],[4,2,1,"","albums"],[4,3,1,"","asdict"],[4,2,1,"","burst"],[4,2,1,"","burst_album_info"],[4,2,1,"","burst_albums"],[4,2,1,"","burst_default_pick"],[4,2,1,"","burst_key"],[4,2,1,"","burst_photos"],[4,2,1,"","burst_selected"],[4,2,1,"","comments"],[4,2,1,"","date"],[4,2,1,"","date_added"],[4,2,1,"","date_modified"],[4,2,1,"","date_trashed"],[4,2,1,"","description"],[4,3,1,"","detected_text"],[4,2,1,"","duplicates"],[4,2,1,"","exif_info"],[4,2,1,"","exiftool"],[4,3,1,"","export"],[4,2,1,"","external_edit"],[4,2,1,"","face_info"],[4,2,1,"","favorite"],[4,2,1,"","filename"],[4,2,1,"","has_raw"],[4,2,1,"","hasadjustments"],[4,2,1,"","hdr"],[4,2,1,"","height"],[4,2,1,"","hidden"],[4,2,1,"","import_info"],[4,2,1,"","incloud"],[4,2,1,"","intrash"],[4,2,1,"","iscloudasset"],[4,2,1,"","ismissing"],[4,2,1,"","ismovie"],[4,2,1,"","isphoto"],[4,2,1,"","israw"],[4,2,1,"","isreference"],[4,3,1,"","json"],[4,2,1,"","keywords"],[4,2,1,"","labels"],[4,2,1,"","labels_normalized"],[4,2,1,"","likes"],[4,2,1,"","live_photo"],[4,2,1,"","location"],[4,2,1,"","moment"],[4,2,1,"","orientation"],[4,2,1,"","original_filename"],[4,2,1,"","original_filesize"],[4,2,1,"","original_height"],[4,2,1,"","original_orientation"],[4,2,1,"","original_width"],[4,2,1,"","owner"],[4,2,1,"","panorama"],[4,2,1,"","path"],[4,2,1,"","path_derivatives"],[4,2,1,"","path_edited"],[4,2,1,"","path_edited_live_photo"],[4,2,1,"","path_live_photo"],[4,2,1,"","path_raw"],[4,2,1,"","person_info"],[4,2,1,"","persons"],[4,2,1,"","place"],[4,2,1,"","portrait"],[4,2,1,"","project_info"],[4,2,1,"","raw_original"],[4,3,1,"","render_template"],[4,2,1,"","score"],[4,2,1,"","screenshot"],[4,2,1,"","search_info"],[4,2,1,"","search_info_normalized"],[4,2,1,"","selfie"],[4,2,1,"","shared"],[4,2,1,"","slow_mo"],[4,2,1,"","time_lapse"],[4,2,1,"","title"],[4,2,1,"","tzoffset"],[4,2,1,"","uti"],[4,2,1,"","uti_edited"],[4,2,1,"","uti_original"],[4,2,1,"","uti_raw"],[4,2,1,"","uuid"],[4,2,1,"","visible"],[4,2,1,"","width"]],"osxphotos.PhotoTemplate":[[4,3,1,"","get_media_type"],[4,3,1,"","get_photo_video_type"],[4,3,1,"","get_template_value"],[4,3,1,"","get_template_value_exiftool"],[4,3,1,"","get_template_value_filter_function"],[4,3,1,"","get_template_value_function"],[4,3,1,"","get_template_value_multi"],[4,3,1,"","get_template_value_pathlib"],[4,3,1,"","render"]],"osxphotos.PhotosDB":[[4,2,1,"","album_info"],[4,2,1,"","album_info_shared"],[4,2,1,"","albums"],[4,2,1,"","albums_as_dict"],[4,2,1,"","albums_shared"],[4,2,1,"","albums_shared_as_dict"],[4,2,1,"","db_path"],[4,2,1,"","db_version"],[4,3,1,"","execute"],[4,2,1,"","folder_info"],[4,2,1,"","folders"],[4,3,1,"","get_db_connection"],[4,3,1,"","get_photo"],[4,2,1,"","import_info"],[4,2,1,"","keywords"],[4,2,1,"","keywords_as_dict"],[4,2,1,"","labels"],[4,2,1,"","labels_as_dict"],[4,2,1,"","labels_normalized"],[4,2,1,"","labels_normalized_as_dict"],[4,2,1,"","library_path"],[4,2,1,"","person_info"],[4,2,1,"","persons"],[4,2,1,"","persons_as_dict"],[4,3,1,"","photos"],[4,3,1,"","photos_by_uuid"],[4,2,1,"","project_info"],[4,3,1,"","query"]],"osxphotos.QueryOptions":[[4,4,1,"","album"],[4,4,1,"","burst"],[4,4,1,"","burst_photos"],[4,4,1,"","cloudasset"],[4,4,1,"","deleted"],[4,4,1,"","deleted_only"],[4,4,1,"","description"],[4,4,1,"","duplicate"],[4,4,1,"","edited"],[4,4,1,"","exif"],[4,4,1,"","external_edit"],[4,4,1,"","favorite"],[4,4,1,"","folder"],[4,4,1,"","from_date"],[4,4,1,"","function"],[4,4,1,"","has_comment"],[4,4,1,"","has_likes"],[4,4,1,"","has_raw"],[4,4,1,"","hdr"],[4,4,1,"","hidden"],[4,4,1,"","ignore_case"],[4,4,1,"","in_album"],[4,4,1,"","incloud"],[4,4,1,"","is_reference"],[4,4,1,"","keyword"],[4,4,1,"","label"],[4,4,1,"","live"],[4,4,1,"","location"],[4,4,1,"","max_size"],[4,4,1,"","min_size"],[4,4,1,"","missing"],[4,4,1,"","missing_bursts"],[4,4,1,"","movies"],[4,4,1,"","name"],[4,4,1,"","no_comment"],[4,4,1,"","no_description"],[4,4,1,"","no_likes"],[4,4,1,"","no_location"],[4,4,1,"","no_place"],[4,4,1,"","no_title"],[4,4,1,"","not_burst"],[4,4,1,"","not_cloudasset"],[4,4,1,"","not_favorite"],[4,4,1,"","not_hdr"],[4,4,1,"","not_hidden"],[4,4,1,"","not_in_album"],[4,4,1,"","not_incloud"],[4,4,1,"","not_live"],[4,4,1,"","not_missing"],[4,4,1,"","not_panorama"],[4,4,1,"","not_portrait"],[4,4,1,"","not_screenshot"],[4,4,1,"","not_selfie"],[4,4,1,"","not_shared"],[4,4,1,"","not_slow_mo"],[4,4,1,"","not_time_lapse"],[4,4,1,"","panorama"],[4,4,1,"","person"],[4,4,1,"","photos"],[4,4,1,"","place"],[4,4,1,"","portrait"],[4,4,1,"","query_eval"],[4,4,1,"","regex"],[4,4,1,"","screenshot"],[4,4,1,"","selected"],[4,4,1,"","selfie"],[4,4,1,"","shared"],[4,4,1,"","slow_mo"],[4,4,1,"","time_lapse"],[4,4,1,"","title"],[4,4,1,"","to_date"],[4,4,1,"","uti"],[4,4,1,"","uuid"],[4,4,1,"","year"]],"osxphotos.SearchInfo":[[4,2,1,"","activities"],[4,2,1,"","all"],[4,3,1,"","asdict"],[4,2,1,"","bodies_of_water"],[4,2,1,"","city"],[4,2,1,"","country"],[4,2,1,"","holidays"],[4,2,1,"","labels"],[4,2,1,"","locality_names"],[4,2,1,"","media_types"],[4,2,1,"","month"],[4,2,1,"","neighborhoods"],[4,2,1,"","place_names"],[4,2,1,"","season"],[4,2,1,"","state"],[4,2,1,"","state_abbreviation"],[4,2,1,"","streets"],[4,2,1,"","venue_types"],[4,2,1,"","venues"],[4,2,1,"","year"]],osxphotos:[[4,1,1,"","AlbumInfo"],[4,1,1,"","AlbumSortOrder"],[4,1,1,"","CommentInfo"],[4,1,1,"","ExifInfo"],[4,1,1,"","ExifTool"],[4,1,1,"","ExportDB"],[4,1,1,"","ExportOptions"],[4,1,1,"","ExportResults"],[4,1,1,"","FileUtil"],[4,1,1,"","FileUtilNoOp"],[4,1,1,"","FolderInfo"],[4,1,1,"","ImportInfo"],[4,1,1,"","LikeInfo"],[4,1,1,"","MomentInfo"],[4,1,1,"","PersonInfo"],[4,1,1,"","PhotoExporter"],[4,1,1,"","PhotoInfo"],[4,1,1,"","PhotoTemplate"],[4,1,1,"","PhotosDB"],[4,1,1,"","PlaceInfo"],[4,1,1,"","ProjectInfo"],[4,1,1,"","QueryOptions"],[4,1,1,"","ScoreInfo"],[4,1,1,"","SearchInfo"],[4,5,1,"","is_debug"],[4,5,1,"","set_debug"],[0,6,1,"cmdoption-osxphotos-db","--db"],[0,6,1,"cmdoption-osxphotos-json","--json"],[0,6,1,"cmdoption-osxphotos-v","--version"],[0,6,1,"cmdoption-osxphotos-v","-v"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","property","Python property"],"3":["py","method","Python method"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["std","cmdoption","program option"]},objtypes:{"0":"py:module","1":"py:class","2":"py:property","3":"py:method","4":"py:attribute","5":"py:function","6":"std:cmdoption"},terms:{"0":[0,2,3,4,5,6],"00":0,"00000":5,"00001":5,"00002":5,"00003":5,"01":[0,3,6],"02":[5,6],"03":5,"04":[5,6],"048576mb":0,"05":6,"05d":5,"07":[0,3,5],"0b16":5,"1":[0,2,4,5,6],"10":[0,2,3,5],"100":6,"1048576":0,"11":[],"12":[0,2,3],"120":4,"1200":3,"128fb4c6":5,"12t12":0,"13":5,"15":[0,2,5],"15mm":5,"18":3,"18th":5,"19":5,"190738":5,"1gb":[],"2":[0,2,3,4,5],"20":0,"2000":0,"20009":5,"2001":0,"2007":5,"2015":6,"2018":6,"2019":[0,6],"2020":[5,6],"2021":6,"2022":[0,3],"21":6,"22":5,"23":5,"28":6,"29":3,"3":[0,2,3,5,6],"31":[0,6],"31581":3,"32":3,"36":[3,5],"38":5,"4":[4,5,6],"41cd":3,"47":[3,5],"4e7d":5,"5":[0,2,3,4,5],"50":6,"54":3,"6":2,"6000":3,"6s":5,"7":5,"75":[4,5],"77":3,"8":2,"8601":0,"9":6,"9108":5,"913095d62e31":3,"94e0":3,"boolean":[4,5,6],"byte":[0,4],"case":[0,4,5,6],"class":[0,3,4,5],"default":[0,3,4,5,6],"do":[0,2,3,4,5,6],"export":[1,2,3,4,5],"final":6,"float":4,"function":[0,3,4,5],"import":[0,3,4,5,6],"int":4,"long":4,"new":[0,2,4,6],"null":[5,6],"public":[4,5],"return":[0,3,4,5],"short":6,"true":[3,4,5,6],"try":6,"vid\u00e9o":5,"vid\u00e9o_acc\u00e9l\u00e9r\u00e9":5,"while":6,A:[0,5,6],AND:[0,4],And:6,As:6,Be:0,By:[0,6],For:[0,4,5,6],If:[0,2,3,4,5,6],In:[0,4,5,6],Is:6,It:[4,5,6],No:[4,6],Not:5,OR:[0,4],Of:6,On:4,One:6,That:6,The:[0,2,3,4,5,6],Then:[2,6],There:6,These:[0,3,5,6],To:[0,2,4,5,6],With:6,_:[5,6],__main__:3,__name__:3,_bearbeiten:0,_edit:[0,6],_low_r:0,_origin:0,_preview:[0,4],abbrevi:[4,5],abil:2,abl:[2,6],about:[1,2,3,4,6],abov:[5,6],absolut:4,access:[4,5,6],accord:2,activ:[4,5],actual:[0,1,4,5],ad:[0,4,5,6],add:[0,4,5,6],addit:[0,3,4,5,6],address:5,addvalu:4,adjust:[4,5,6],adjustmentsinfo:4,adob:0,advanc:6,advantag:0,aesthet:[3,5],af71:3,affect:4,after:[0,4,5,6],again:6,against:[4,6],ahead:4,aka:2,album1:5,album:[1,2,3,4,5,6],album_info:4,album_info_shar:4,album_nam:3,album_project:5,album_seq:5,albuminfo:[3,4],albums_as_dict:[3,4],albums_shar:4,albums_shared_as_dict:4,albumsortord:4,algorithm:[5,6],alia:5,alic:3,all:[0,1,2,3,4,5],all_fil:4,all_photo:3,allow:[0,2,5,6],almost:6,alpha:0,alreadi:[0,4,6],also:[0,2,3,4,5,6],altern:[0,6],alwai:6,amount:6,an:[0,1,3,4,5],ani:[0,2,3,4,5,6],anoth:[5,6],anyth:6,apertur:4,apf:0,api:1,app:[0,2,4,6],appear:4,append:[0,4,5,6],appl:[0,2,4,5,6],applescript:[0,4,6],appli:[0,4,5,6],applic:[2,6],april:6,ar:[0,3,4,5,6],area:5,area_of_interest:5,aren:4,arg:[0,2,4],argument:[0,2,3,6],asdict:4,ask:0,asset:[4,5,6],assign:[5,6],associ:[0,2,4,5,6],assum:[4,5],asynchroun:4,attach:6,attempt:[0,2,4,6],attribut:[0,1],author:[0,6],auto:3,automat:[0,5,6],avail:[0,4,5,6],awar:4,b13f4485:3,back:[4,5,6],backup:6,bar:[0,3,5],bare:5,base:[3,4,5,6],basi:6,basic:[4,6],beach:[0,5],beachdai:5,becaus:6,been:[0,3,4,5,6],befor:[0,4,5,6],begin:[0,5],behavior:[0,4],being:[0,4,5,6],belong:4,below:[0,4],best:[0,4,6],beta:[],better:6,between:[4,5,6],bin:6,bind:0,bit:4,bit_flag:4,bit_rat:4,bitmath:4,blank:6,blob:[0,5],bodi:4,bodies_of_wat:4,bool:4,bool_val:4,bool_valu:5,boost:6,both:[0,2,5,6],brace:[5,6],bracket:5,brew:[0,2],browser:[0,2],buggi:6,built:5,burst:[0,4,5],burst_album:4,burst_album_info:4,burst_default_pick:4,burst_kei:4,burst_photo:4,burst_select:4,cach:5,calendar:[4,5],call:[0,2,4,5,6],callabl:4,caller:4,camera:[0,4,5],camera_mak:[4,5],camera_model:[4,5],can:[0,2,3,4,5,6],cannot:[0,4,5],canon:[0,4],capabl:6,capit:5,caption:6,card:[4,5],care:[],carri:6,carriag:5,catalina:[0,5],categor:[4,5],categori:[0,6],caus:[5,6],caution:0,cd:[],certain:[0,1,5],certainli:6,chang:[0,4,5,6],charact:[3,5,6],check:[0,2],chose:6,christma:5,citi:[4,5],clang:3,classic:6,classif:[0,2,6],classifi:6,classmethod:4,cleanup:0,cli:[1,2],click:3,clone:0,close:[4,5],closebrac:5,closebracket:5,closeparen:5,cloud:[4,6],cloudasset:[0,4],cmp:4,cmp_file_sig:4,code:[1,2,5],codec:4,collis:0,colon:5,color:[0,1,2],com:[0,4,5],combin:[5,6],comfort:[],comma:[5,6],command:[1,3,4,5],command_nam:2,comment:[0,3,4,5,6],commentinfo:4,common:6,commun:6,compar:[0,2,4],comparison:5,compat:[0,6],complet:[4,6],complex:[0,6],compon:0,comprehens:[0,6],compress:[0,4],compression_qu:4,comput:[3,4],concept:6,conclus:1,condit:[5,6],confid:[4,5],confidence_threshold:4,config:[0,4,6],config_data:4,configur:[0,6],confirm:0,conform:4,conjunct:[0,6],connect:[0,4,6],consist:0,contact:[],contain:[0,3,4,5,6],content:4,context:[0,4],contribut:6,convert:[0,1,4,5],convert_to_jpeg:4,converted_to_jpeg:[0,4],coordin:[0,6],copi:[0,3,4,6],copyright:[0,6],correct:[4,6],correspond:0,could:[0,3,5,6],count:[4,5],countri:[4,5,6],country_cod:5,cours:6,cover:6,cr2:4,cr:5,crash:[0,6],creat:[0,1,2,3,4,5],create_file_record:4,create_or_get_file_record:4,created:0,creation:[4,5,6],creationd:0,creator:0,criteria:[0,4,6],crlf:5,crop:6,csv:[0,6],curat:4,curli:[5,6],current:[0,4,5,6],cursor:4,custom:[3,5,6],cut:6,d:5,dai:[0,5,6],dark:[0,6],data:[0,4,5,6],databas:[0,2,3,4,5,6],date:[0,1,3,4,5],date_ad:4,date_modifi:4,date_trash:4,datecr:0,datetim:4,datetimeorigin:[0,4],daunt:6,db2:0,db:[0,2,3,4,6],db_path:4,db_version:4,dbfile:4,dbpath:6,dc:5,dd:[5,6],debug:[0,2,4],decid:6,decreas:6,def:3,default_album:3,defin:[0,3,5,6],degre:4,delai:4,delet:[0,4],deleted_directori:4,deleted_fil:4,deleted_onli:4,delim:[4,5],delimit:[4,5],demonstr:6,depend:[0,5],deriv:4,desc:0,descend:4,descr:[0,5,6],describ:6,descript:[0,2,3,4,5,6],description_templ:4,design:6,desir:6,desktop:6,dest:[0,4],dest_dir:3,dest_fil:4,destin:[3,4],destruct:4,detail:[0,3,4,6],detect:[3,4,5],detected_text:[4,5],determin:[4,6],develop:3,devic:6,dict:4,dictionari:4,did:[0,6],diff:[1,2],differ:[0,2,4,6],digikam:[0,6],digit:[5,6],dir:[0,3,4],direct:[0,5],directli:[0,2,6],directori:[0,1,3,4,5],dirpath:4,disabl:[],discuss:6,disk:[0,1,4],displai:[0,2,5],distinguish:6,dng:6,doc:[1,2],document:[0,2,5],doe:[0,3,4,5,6],doesn:[0,6],doi:5,don:[0,6],done:[3,6],dot:6,dow:5,down:[5,6],download:[0,4,6],download_miss:4,drive:[0,6],drop:[0,4,6],dry:[0,1,4],dry_run:4,dsc05678:6,dump:[1,2],duplic:[0,3,4,5],durat:4,dure:[0,4,6],dynam:0,e:[0,2,3,4,5,6],each:[0,4,5,6],earlier:4,easi:6,easiest:2,easili:[2,6],echo:[0,3,6],edit:[0,1,2,3,4,5],edited_filenam:[],edited_nam:3,edited_vers:5,editor:[0,4],either:[0,2,4,5,6],els:[3,4,6],emac:0,embed:[0,6],empti:[4,5,6],en:[],enabl:6,enclos:5,encount:6,end:[0,4,5],end_dat:4,endswith:5,ensur:[4,6],entir:[0,4,6],environ:[0,2,3],equal:5,equival:0,error:[0,4,6],error_str:[],errror:4,etc:[0,2,4,5,6],eval:0,evalu:[0,3,4,5,6],even:[0,4,6],event:5,everi:6,everyth:6,exact:4,exactli:[5,6],exampl:[0,1,2,4,5],excel:6,except:4,execut:[4,5,6],exif:[0,3,4,5],exif_info:4,exif_tag:0,exif_upd:[0,4],exifinfo:4,exiftool:[0,3,4,5,6],exiftool_error:4,exiftool_flag:4,exiftool_path:[0,4],exiftool_pod:0,exiftool_warn:4,exiftoolcach:4,exist:[0,3,4,6],exit:[0,2,3,4],expand:[4,5,6],expandus:3,expect:[0,6],experi:0,experiment:[0,6],explain:6,explan:6,explicitli:6,explor:[3,6],export2:[],export_as_hardlink:4,export_db:4,export_dir:[0,4,6],export_path:3,exportdb:[0,4,6],exportdb_abc:[],exportdb_fil:0,exportdbinmemori:4,exported_album:4,exportopt:[3,4],exportrecord:4,exportresult:[3,4],exposure_bia:4,express:[0,4,5,6],ext:[0,6],extend:[0,4,6],extens:[0,4,6],extern:[0,1,4,5],external_edit:4,extract:[4,5],f1:4,f:[0,3,4,5],face:[0,2,3,4,5,6],face_info:4,face_region:4,faceinfo:4,fail:4,failur:4,fall:4,fals:[4,5,6],familiar:[],faster:[0,4,6],favorit:[0,4,5,6],fb2e90da1546:5,featur:[0,3,5,6],feb:3,februari:5,feed:5,few:6,field:[4,5,6],file1:4,file:[0,1,2,3,4,5],file_cmp:4,file_sig:4,filenam:[0,1,3,4,5],filename_origin:0,filepath:[0,3,4,6],files:4,filesystem:[4,6],fileutil:4,fileutilabc:4,fileutilnoop:4,filter:[0,3,4,5,6],filter_:4,find:[0,2,3,4,5,6],finder:[0,1],findercom:[0,6],first:[0,4,5,6],flag:[0,4],flaki:6,flash_fir:4,flexibl:[2,6],focal_length:4,focu:6,folder1:[5,6],folder1album1:5,folder2:6,folder:[0,3,4,5,6],folder_album:[0,4,5,6],folder_album_project:5,folder_album_seq:5,folder_info:4,folder_list:4,folder_nam:4,folderinfo:4,follow:[0,2,3,5,6],foo:[3,5],foobar:5,forc:[0,4,6],force_upd:4,form:[0,4,5,6],format:[0,2,4,5,6],foto:5,found:[0,2,3,4,5,6],fp:4,free:[5,6],from:[0,1,2,3,4,5],from_dat:[0,4],from_tim:[0,4],front:[0,4],full:[0,4,5,6],function_nam:5,futur:[5,6],g:[0,2,3,4,5,6],gener:[0,4,6],geoloc:[0,4,5,6],get:[2,3,4,5,6],get_db_connect:4,get_file_record:4,get_media_typ:4,get_photo:[3,4],get_photo_video_typ:4,get_photoinfo_for_uuid:4,get_previous_uuid:4,get_select:3,get_template_valu:4,get_template_value_exiftool:4,get_template_value_filter_funct:4,get_template_value_funct:4,get_template_value_multi:4,get_template_value_pathlib:4,get_uuid_for_fil:4,github:[0,5],give:0,given:4,good:6,gopro:6,gp:[0,6],gpscoordin:0,gpslatitud:0,gpslatituderef:0,gpslongitud:0,gpslongituderef:0,gpsposit:0,gpu:0,greater:5,greet:5,group:[0,4,5],h:[0,2,5],ha:[0,3,4,5,6],had:[0,6],handl:[0,6],happen:0,hard:6,hardlink:[0,4],harmonious_color:4,has_com:4,has_lik:4,has_raw:4,hasadjust:[3,4],hash:0,hasn:6,have:[0,3,4,5,6],haven:0,hdr:[0,4,5],header:[],headlin:[0,6],heic:0,height:[0,4],help:[1,2,3,5,6],here:[0,6],hidden:[0,4,6],hierarch:[4,6],high:0,highest:4,highli:0,highlight:0,highlight_vis:4,hold:[4,5,6],holidai:[4,5],home:6,homebrew:[0,2],hopefulli:6,hour:5,how:[0,5,6],howev:6,html:0,http:[0,2,4,5,6],i:[0,4,5,6],icloud:[0,4,6],id:[0,2,4,5],identifi:[0,4,5,6],ignor:[0,4,6],ignore_cas:4,ignore_date_modifi:4,ignore_signatur:4,imag:[0,1,2,4,5],imagedescript:0,imagenam:5,img_1234:[0,4,6],img_1234_2021:6,img_1234_edit:6,immedi:[4,6],immers:4,implement:[0,4,5,6],import_info:4,importinfo:4,improv:[0,6],in_album:4,incloud:[0,4],includ:[0,2,3,4,5,6],incorrect:[4,5],increment:4,index:[1,4,5],indic:5,info:[1,2,4],inform:[0,2,3,4,5,6],initi:4,insensit:[0,5],insert:6,insid:6,inspect:[0,3],instal:[1,3,4,5,6],instanc:[3,4,6],instead:[0,4,5,6],instruct:[2,6],integ:[0,5],integr:6,intend:[0,6],intent:6,interact:[0,2,3,4],interest:[5,6],interesting_subject:4,interfac:[1,2,3,4,6],intermitt:0,intern:[5,6],internet:0,interpret:6,interrupt:[0,6],intrash:4,intrusive_object_pres:4,invalid:[3,4],invest:6,involv:6,io:[0,5],iphon:[5,6],iptc:[0,4,5],is_debug:4,is_refer:4,is_valid_filepath:3,iscloudasset:4,isdir:3,ishdr:5,ismin:4,ismiss:[3,4],ismovi:4,isn:6,iso:[0,4,5],isphoto:4,israw:4,isrefer:4,item:[0,4],iter:4,iterm2:0,its:[4,5,6],itself:[5,6],j:[0,4],john:3,join:[3,5,6],jpeg:[0,1,4,5],jpeg_ext:4,jpeg_qual:[0,4],jpg:[0,3,4,5,6],json:[0,2,4,6],julian:5,just:[0,3,5,6],keep:6,kei:[4,5,6],keybind:0,keyword:[1,2,3,4,5,6],keyword_templ:4,keywords_as_dict:[3,4],kind:6,know:[0,4],knowledg:5,known:6,label:[1,2,3,4,5,6],label_norm:5,labels_as_dict:4,labels_norm:4,labels_normalized_as_dict:4,landmark:5,languag:5,laps:[0,4],larg:[0,6],largest:4,last:[0,2,3,6],later:[0,5],latest:0,latitud:4,launch:0,lead:[0,4,6],learn:[4,5,6],left:6,legibl:6,len:[3,5],lens_model:[4,5],less:[5,6],level:[0,4],lf:5,librari:[0,1,2,3,4],library_path:[3,4],licens:[0,2],light:0,lightroom:[0,6],liglob:4,like:[0,3,4,5,6],likeinfo:4,likewis:3,limit:[5,6],line:[1,3,5,6],link:4,list:[1,2,3,4,5,6],liter:[4,6],littl:6,live:[0,4,5,6],live_photo:[4,5],lively_color:4,ll:[0,6],load:[0,1,3],local:[4,5,6],locality_nam:4,locat:[0,4,6],lock:3,log:[4,6],logic:6,longitud:4,look:[0,2,4,6],lookup:[4,6],loop:3,lose:[0,6],lot:0,low_light:4,lower:[0,4,5],lowest:4,m:[0,4,5],mac:[0,2,6],machin:[0,2,4,5,6],maco:[0,2,5],made:5,mai:[0,3,4,5,6],main:3,maintain:6,make:[0,3,5,6],makedir:3,manag:[0,2,4,6],mangl:4,mani:[0,6],manner:6,manual:6,mark:[0,4,5,6],markup:4,master:[0,5],match:[0,4,5,6],matter:6,max:0,max_siz:4,maximum:[0,4],me:[],mean:[4,5],media:[4,5,6],media_typ:[4,5],memori:[0,4],mention:6,mere:6,merg:[0,4,6],merge_exif_keyword:4,merge_exif_person:4,messag:[0,2],metadata:[0,1,2,4,5],metering_mod:4,method:4,mib:0,microsoft:[0,6],might:[4,6],min:[0,5],min_siz:4,minimum:4,minor:0,minut:[5,6],miss:[0,1,3,4],missing_album:4,missing_burst:4,mkdtemp:4,mm:[5,6],mo:[0,4],mock:4,mode:[0,4,6],model:[4,5],modif:[0,4,5,6],modifi:[0,4,5,6],modification_d:4,modifyd:[0,4],modul:1,moment:[3,4],moment_pk:4,momentinfo:[3,4],mon:5,mono:0,monokai:0,monterei:[2,5],month:[4,5,6],more:[0,2,3,4,5,6],most:[0,6],motion:[0,4],mov:[0,4],move:6,movi:[0,4,6],mtime:4,much:6,multi:[0,4,5,6],multipl:[0,5,6],must:[0,4,5,6],my:[5,6],myalternatelibrari:6,mysteri:4,n:[4,5,6],na:6,naiv:4,name:[0,2,3,4,5,6],nativ:6,ndescript:6,necessari:3,need:[0,3,5,6],negat:5,neighborhood:4,neither:[0,2,4],nest:6,network:[0,6],never:6,nevertheless:6,new_them:0,newer:0,newli:4,newlin:[5,6],next:6,nist:0,no_com:4,no_descript:4,no_fil:4,no_lik:4,no_loc:4,no_plac:4,no_titl:4,nois:4,nolabel:6,non:[0,4,6],none:[3,4],nonetyp:[],nor:0,normal:[0,4,6],norsrc:4,not_burst:4,not_cloudasset:4,not_favorit:4,not_hdr:4,not_hidden:4,not_in_album:4,not_incloud:4,not_liv:4,not_miss:4,not_panorama:4,not_portrait:4,not_screenshot:4,not_selfi:4,not_shar:4,not_slow_mo:4,not_time_laps:4,note:[0,4,5,6],noth:6,nothdr:5,notic:4,now:[2,6],number:[0,5,6],nw:5,object:[0,3,4,5],objexplor:3,obvious:0,occasion:6,occur:[0,4,5],offer:6,offset:4,offsettimeorigin:0,often:6,older:4,one:[0,4,5,6],onli:[0,1,2,4,5],op:4,open:[0,2,3,5,6],openbrac:5,openbracket:5,openparen:5,oper:[1,5,6],oppos:6,optim:6,option:[0,1,2,3,4,5],order:[0,2,4],org:[0,4,5,6],organ:[0,6],orient:4,origin:[0,2,3,4,5,6],original_filenam:[3,4],original_files:4,original_height:4,original_nam:[0,5,6],original_orient:4,original_width:4,os:[2,3,4],oserror:4,osxphoto:[],osxphotos_cmd_lin:5,osxphotos_export:[0,6],osxphotos_snapshot:0,osxphotos_vers:5,ot:6,other:[0,4,5,6],otherwis:[0,3,4,5,6],out:[0,2,6],output:[0,2,4,5,6],outsid:4,over:[0,6],overal:[4,5],overrid:[0,5],overview:1,overwrit:[0,4],own:[0,3,6],owner:4,p:3,packag:[0,1,2],pad:5,page:1,pair:[0,4,5,6],panorama:[0,4,5],paramet:4,paren:5,parent:4,parenthes:5,part:[0,4,5,6],partial:5,particip:0,particular:4,particularli:0,pass:[0,3,4,5,6],path:[0,2,3,4,5,6],path_deriv:4,path_edit:[3,4],path_edited_live_photo:4,path_live_photo:4,path_raw:4,path_sep:[4,5],pathlib:[3,4],pathvalid:3,pattern:6,per:0,perform:[0,5,6],period:[5,6],permit:0,person:[1,2,3,4,5,6],person_info:4,personinfo:4,personinimag:0,persons_as_dict:[3,4],philipp:6,philosophi:6,photo:[0,1,2,3,4,5],photo_ext:0,photo_filenam:0,photo_index:4,photo_or_video:5,photoexport:[3,4],photoinfo:[0,3,4,5],photokit:[0,4,6],photonam:[0,6],photoname_bearbeiten:0,photoname_edit:0,photoname_low_r:0,photoname_preview:0,photopr:6,photos_by_uuid:4,photos_librari:[0,2],photosdb:[3,4],photoshop:0,photoslibrari:[0,2,3,6],phototempl:4,phrase:5,pick:6,pictur:[0,2,3,4,6],pid:4,pip:3,pipe:5,pipx:[2,3],pixel:4,pk:4,place:[1,2,4,5,6],place_nam:4,placeinfo:[3,4],plain:[0,6],platform:3,pleas:[],pleasant_camera_tilt:4,pleasant_composit:4,pleasant_light:4,pleasant_pattern:4,pleasant_perspect:4,pleasant_post_process:4,pleasant_reflect:4,pleasant_symmetri:4,png:0,popul:6,portrait:[0,4,5,6],posit:[0,2],possibl:[0,4,6],post:[0,1,3],postal:5,postal_cod:5,posttext:5,power:6,pre:[],preced:[0,5,6],prefer:6,prefix:4,present:[0,5],preserv:6,pretext:5,preview:[0,4,5,6],preview_suffix:4,previou:1,previous:[0,4,6],primari:[0,4,5],print:[0,2,3,4,6],privat:0,pro:6,probabl:6,process:[0,1,3,4,5],produc:[4,6],program:[0,6],programmat:3,progress:0,project:[0,4,5],project_info:4,projectinfo:4,promot:4,proper:6,properli:6,properti:[4,5],prototyp:0,provid:[0,2,4,5,6],provinc:5,purpos:6,put:6,py:[0,5],pygment:0,pylint:[],pypi:[],python3:[],python:[0,1,2,5],python_fil:0,q:3,qualiti:[0,4,6],queri:[1,2,3,4,6],query_ev:4,query_funct:0,queryopt:[3,4],question:5,questionmark:5,quickli:[0,6],quicktim:[0,4],quik:6,quit:[3,6],quot:[5,6],r:[0,5],rais:4,ram:6,ramdb:[0,6],rang:[0,4,6],rate:0,rather:6,raw:[0,4,6],raw_origin:4,raw_photo:4,re:[0,6],read:[2,4,6],readabl:[4,6],readthedoc:[],reason:6,recent:[0,4],recommend:[2,6],record:4,refer:[0,4,5,6],referenc:0,regardless:4,regex:[0,4,6],region:[0,4],regular:[0,4,5,6],regularli:6,relat:4,releas:[],reliabl:0,rememb:6,remov:[0,4,6],renam:[0,4,5,6],render:[0,4,5,6],render_opt:4,render_templ:4,rendered_str:4,renderopt:4,repeat:[0,6],repeatedli:6,repl:[1,2],replac:[0,4,5,6],replace_keyword:4,repo:[],report:[0,1],repres:[0,4,5],represent:4,reprocess:5,requir:[0,2,4,5,6],resolut:0,resolv:5,respect:4,restart:6,restaur:5,result:[0,3,4,5,6],resum:6,retri:[0,6],retriev:6,reus:0,revers:[0,4,5,6],rhet:[],rhettbul:[0,5],rich:[4,6],rise:6,rmdir:4,rn:5,roll:6,rule:4,run:[1,2,4,5],run_command:4,s:[0,2,4,5,6],safe:5,same:[0,2,3,4,5,6],sample_r:4,sanitize_filepath:3,save:[0,1],score:[3,4,5],scoreinfo:[3,4],screenshot:[0,4,5,6],script:[0,3],search:[0,1,2,4,6],search_info:4,search_info_norm:4,searchinfo:[3,4,5],season:[4,5],sec:5,second:[3,4,5],section:6,secur:4,see:[0,4,5,6],seem:6,seen:6,select:[0,2,3,4,6],self:4,selfi:[0,4,5],semicolon:5,sensit:5,separ:[4,5,6],seq:5,sequenc:5,sequenti:5,session:[3,4],set:[0,4,5,6],set_config:4,set_debug:4,set_photoinfo_for_uuid:4,setup:[],setuptool:[],setvalu:4,sever:6,sh:[0,2],share:[0,3,4,5,6],sharply_focused_subject:4,shell:[0,2,3,5,6],shell_quot:[0,5,6],shot:0,should:[0,2,5,6],show:[0,2,3,4,6],shutter_spe:4,si:0,sidecar:[0,1,4],sidecar_drop_ext:4,sidecar_exiftool:4,sidecar_exiftool_skip:[0,4],sidecar_exiftool_written:[0,4],sidecar_ext:[0,6],sidecar_json:4,sidecar_json_skip:[0,4],sidecar_json_written:[0,4],sidecar_xmp:4,sidecar_xmp_skip:[0,4],sidecar_xmp_written:[0,4],sierra:2,signatur:[0,4,6],signific:6,silent:4,similar:5,simpl:3,simplest:5,simpli:6,sinc:[0,6],singl:[0,4,5,6],size:[0,4,6],skip:[0,3,4,6],skipped_album:4,slideshow:[4,5],slow:[0,4,5,6],slow_mo:[4,5],smart:6,smith:3,snap:[1,2],snapshot:[0,2],so:[4,5,6],softwar:0,some:[0,4,5,6],someth:[4,6],somewhat:6,sort:4,sort_ord:4,sourc:4,space:[0,5,6],special:[4,5,6],specif:[0,2,4,6],specifi:[0,1,2,3,4,5],spend:6,sport:5,spot:6,spotlight:[0,2,3,6],sql:4,sqldiff:0,sqlite3:4,sqlite:3,src:4,src_file:4,st:5,stabl:[],stack:4,stamp:0,standard:6,start:[0,3,4,5,6],start_dat:4,startswith:5,stat:4,state:[0,4,5,6],state_abbrevi:4,state_provinc:5,statement:[4,5,6],statu:[0,4],still:[4,6],stop:4,storag:6,storat:4,store:[0,4,6],str:4,street:[4,5],strftime:5,string:[0,4,5,6],strip:[0,4,5,6],struct:4,structur:1,style:[0,6],sub:[4,5],subfield:[4,5],subfold:[0,4,5],subject:0,subsequ:0,subset:6,substitut:[1,4],subtitl:4,subtop:0,succeed:4,success:4,suffix:[0,4,6],suit:6,suitabl:0,summer:[5,6],suppli:4,support:[1,5],sur:[],sure:[0,3,6],sy:3,symbol:5,synch:[0,4,6],syntax:[0,5,6],system:[0,1,4,6],t:[0,4,6],tab:5,tabl:4,tag:[0,2,4,5,6],tag_group:4,tagnam:[0,5,6],tagslist:0,take:[5,6],taken:[0,4,6],tastefully_blur:4,tell:6,tempfil:4,templat:[0,1,3,4,6],template_field:5,template_filt:5,template_funct:5,template_str:4,temporari:[0,3,4],temporarydirectori:4,term:[4,6],termin:[0,2,6],test:[0,2,3,4,6],text:[4,5,6],text_detection_confidence_threshold:4,than:[0,3,4,5,6],thank:6,thei:[0,4,6],them:[0,4,6],theme:[1,2],thi:[0,2,3,4,5,6],thing:[5,6],think:6,those:[3,6],thousand:6,threshold:[4,5],through:[2,6],throughout:6,thu:[0,6],time:[0,4,5,6],time_laps:[4,5],timecr:0,timeout:4,timestamp:0,timezon:[0,4],titl:[0,3,4,5,6],titlecas:5,titlen:6,tmp:[0,4],tmpdir:[0,4],to_dat:[0,4],to_tim:[0,4],to_touch:4,todai:[0,5],togeth:6,toml:[0,6],took:3,tool:6,top:[0,4],topic:0,touch:[0,4,6],touch_fil:4,town:4,track:6,track_format:4,trail:[0,6],trash:[3,4],travel:[5,6],treat:[0,4,5,6],tremend:6,trigger:[0,4],tupl:4,tutori:[1,2],two:[0,2,6],txt:[0,6],type:[0,2,3,4,5,6],typeerror:4,tzoffset:4,u:[0,5],underli:5,underscor:6,understand:6,unedit:[3,6],unfil:3,uniform:[0,4],uninstal:[1,2,3],union:4,uniqu:[0,2,5],unit:[0,5],univers:5,unless:4,unlik:[0,5,6],unlink:4,unmatch:4,unmodifi:5,unquot:5,unreli:6,until:4,up:[0,4,6],updat:[0,1,4,5],upgrad:0,upon:[0,4,6],upper:5,us:[0,1,2,4,5,6],usag:[0,1,3,5],use_albums_as_keyword:4,use_persons_as_keyword:4,use_photokit:4,use_photos_export:4,useabl:0,user:[1,3,4,5],userdata:0,usr:6,usual:6,utc:4,uti:[0,4],uti_edit:4,uti_origin:4,uti_raw:4,util:[2,4],utim:4,uuid:[1,2,3,4,5],v:[0,2],vacat:[4,5,6],valid:[0,3,4,5,6],valu:[0,4,5,6],value_if_fals:5,value_if_tru:5,valueerror:4,variabl:[0,3],varieti:6,variou:4,ve:[0,4],venu:[4,5],venue_typ:[4,5],verbos:[0,4,6],veri:[2,6],verifi:[3,6],versa:2,version:[1,2,3,4,5,6],vertic:5,vi:0,via:[0,4,6],vice:2,video:[0,4,5,6],viewer:3,virtual:[0,3],visbl:4,visibl:4,volum:[0,6],wa:[0,4,5,6],wai:[2,6],want:[0,3,5,6],warn:[0,4],washington:5,water:4,websit:6,wed:4,week:5,well:[0,2,3,4,5,6],well_chosen_subject:4,well_framed_subject:4,well_timed_shot:4,went:0,were:[0,4,6],what:[0,1,4,5,6],when:[0,4,5,6],where:[0,5,6],whether:0,which:[0,3,4,5,6],white:5,white_bal:4,whitespac:[0,4,5,6],whose:0,wide:6,width:[0,4],wipe:6,within:0,without:[0,4,6],won:6,word:5,work:[0,2,4,5,6],workflow:6,worst:6,worth:6,would:[0,4,5,6],write:[0,4,6],written:[0,4,5,6],x:2,xattr:[0,6],xattr_skip:4,xattr_written:4,xmp:[0,4,6],y:[0,5],ye:0,year:[0,4,5,6],yet:[4,5,6],you:[0,2,3,4,5,6],your:[0,1,2,3,5],yy:5,zero:5},titles:["OSXPhotos Command Line Interface (CLI)","Welcome to OSXPhotos\u2019s documentation!","OSXPhotos","OSXPhotos Python Package Overview","OSXPhotos python API","OSXPhotos Template System","OSXPhotos Tutorial"],titleterms:{"200mb":[],"4":[],"5":[],"default":[],"export":[0,6],about:0,actual:6,add:[],album:0,all:6,an:6,api:4,ar:[],attribut:6,awail:[],base:[],big:[],certain:6,chang:[],cli:[0,3],code:3,color:6,command:[0,2,6],conclus:6,convert:6,countri:[],creat:6,creation:[],date:6,desktop:[],diff:0,digit:[],directori:6,disk:6,doc:0,document:1,dry:6,dump:0,edit:6,exampl:[3,6],exif:[],extern:6,file:6,filenam:6,find:[],finder:6,folder:[],from:6,full:[],git:[],group:[],help:0,higher:[],imag:6,indic:1,info:0,instal:[0,2],interfac:0,jpeg:6,json:[],keyword:0,kid:[],label:0,larger:[],librari:6,line:[0,2],list:0,load:6,media:[],metadata:6,miss:6,modul:[],month:[],name:[],necessari:[],nocountri:[],onli:6,oper:2,option:6,osxphoto:[0,1,2,3,4,5,6],ouput:[],output:[],overview:[3,6],packag:3,person:0,photo:6,pip:[],pipx:[],place:0,post:6,previou:6,print:[],process:6,python:[3,4],queri:0,repl:[0,3],report:6,repositori:[],result:[],run:[0,3,6],s:1,save:6,sidecar:6,snap:0,specifi:6,structur:6,substitut:5,support:2,system:[2,5],tabl:1,templat:5,than:[],them:[],theme:[0,6],tutori:[0,6],type:[],uninstal:0,updat:6,us:3,usag:2,user:6,uuid:0,verbos:[],version:0,video:[],welcom:1,what:2,when:[],write:[],year:[],your:6}})
\ No newline at end of file
+Search.setIndex({docnames:["cli","index","overview","package_overview","reference","template_help","tutorial"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["cli.rst","index.rst","overview.rst","package_overview.rst","reference.rst","template_help.rst","tutorial.rst"],objects:{"":[[4,0,0,"-","osxphotos"]],"osxphotos-albums":[[0,6,1,"cmdoption-osxphotos-albums-db","--db"],[0,6,1,"cmdoption-osxphotos-albums-json","--json"],[0,6,1,"cmdoption-osxphotos-albums-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-diff":[[0,6,1,"cmdoption-osxphotos-diff-db","--db"],[0,6,1,"cmdoption-osxphotos-diff-r","--raw-output"],[0,6,1,"cmdoption-osxphotos-diff-s","--style"],[0,6,1,"cmdoption-osxphotos-diff-V","--verbose"],[0,6,1,"cmdoption-osxphotos-diff-V","-V"],[0,6,1,"cmdoption-osxphotos-diff-r","-r"],[0,6,1,"cmdoption-osxphotos-diff-s","-s"],[0,6,1,"cmdoption-osxphotos-diff-arg-DB2","DB2"]],"osxphotos-dump":[[0,6,1,"cmdoption-osxphotos-dump-db","--db"],[0,6,1,"cmdoption-osxphotos-dump-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-dump-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-dump-json","--json"],[0,6,1,"cmdoption-osxphotos-dump-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-export":[[0,6,1,"cmdoption-osxphotos-export-add-exported-to-album","--add-exported-to-album"],[0,6,1,"cmdoption-osxphotos-export-add-missing-to-album","--add-missing-to-album"],[0,6,1,"cmdoption-osxphotos-export-add-skipped-to-album","--add-skipped-to-album"],[0,6,1,"cmdoption-osxphotos-export-album","--album"],[0,6,1,"cmdoption-osxphotos-export-album-keyword","--album-keyword"],[0,6,1,"cmdoption-osxphotos-export-burst","--burst"],[0,6,1,"cmdoption-osxphotos-export-cleanup","--cleanup"],[0,6,1,"cmdoption-osxphotos-export-config-only","--config-only"],[0,6,1,"cmdoption-osxphotos-export-convert-to-jpeg","--convert-to-jpeg"],[0,6,1,"cmdoption-osxphotos-export-current-name","--current-name"],[0,6,1,"cmdoption-osxphotos-export-db","--db"],[0,6,1,"cmdoption-osxphotos-export-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-export-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-export-description","--description"],[0,6,1,"cmdoption-osxphotos-export-description-template","--description-template"],[0,6,1,"cmdoption-osxphotos-export-directory","--directory"],[0,6,1,"cmdoption-osxphotos-export-download-missing","--download-missing"],[0,6,1,"cmdoption-osxphotos-export-dry-run","--dry-run"],[0,6,1,"cmdoption-osxphotos-export-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-export-edited","--edited"],[0,6,1,"cmdoption-osxphotos-export-edited-suffix","--edited-suffix"],[0,6,1,"cmdoption-osxphotos-export-exif","--exif"],[0,6,1,"cmdoption-osxphotos-export-exiftool","--exiftool"],[0,6,1,"cmdoption-osxphotos-export-exiftool-merge-keywords","--exiftool-merge-keywords"],[0,6,1,"cmdoption-osxphotos-export-exiftool-merge-persons","--exiftool-merge-persons"],[0,6,1,"cmdoption-osxphotos-export-exiftool-option","--exiftool-option"],[0,6,1,"cmdoption-osxphotos-export-exiftool-path","--exiftool-path"],[0,6,1,"cmdoption-osxphotos-export-export-as-hardlink","--export-as-hardlink"],[0,6,1,"cmdoption-osxphotos-export-export-by-date","--export-by-date"],[0,6,1,"cmdoption-osxphotos-export-exportdb","--exportdb"],[0,6,1,"cmdoption-osxphotos-export-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-export-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-export-filename","--filename"],[0,6,1,"cmdoption-osxphotos-export-finder-tag-keywords","--finder-tag-keywords"],[0,6,1,"cmdoption-osxphotos-export-finder-tag-template","--finder-tag-template"],[0,6,1,"cmdoption-osxphotos-export-folder","--folder"],[0,6,1,"cmdoption-osxphotos-export-force-update","--force-update"],[0,6,1,"cmdoption-osxphotos-export-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-export-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-export-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-export-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-export-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-export-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-export-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-export-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-export-ignore-date-modified","--ignore-date-modified"],[0,6,1,"cmdoption-osxphotos-export-ignore-signature","--ignore-signature"],[0,6,1,"cmdoption-osxphotos-export-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-export-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-export-jpeg-ext","--jpeg-ext"],[0,6,1,"cmdoption-osxphotos-export-jpeg-quality","--jpeg-quality"],[0,6,1,"cmdoption-osxphotos-export-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-export-keyword-template","--keyword-template"],[0,6,1,"cmdoption-osxphotos-export-label","--label"],[0,6,1,"cmdoption-osxphotos-export-live","--live"],[0,6,1,"cmdoption-osxphotos-export-load-config","--load-config"],[0,6,1,"cmdoption-osxphotos-export-location","--location"],[0,6,1,"cmdoption-osxphotos-export-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-export-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-export-missing","--missing"],[0,6,1,"cmdoption-osxphotos-export-name","--name"],[0,6,1,"cmdoption-osxphotos-export-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-export-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-export-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-export-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-export-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-export-no-progress","--no-progress"],[0,6,1,"cmdoption-osxphotos-export-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-export-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-export-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-export-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-export-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-export-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-export-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-export-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-export-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-export-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-export-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-export-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-export-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-export-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-export-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-export-only-new","--only-new"],[0,6,1,"cmdoption-osxphotos-export-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-export-original-suffix","--original-suffix"],[0,6,1,"cmdoption-osxphotos-export-overwrite","--overwrite"],[0,6,1,"cmdoption-osxphotos-export-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-export-person","--person"],[0,6,1,"cmdoption-osxphotos-export-person-keyword","--person-keyword"],[0,6,1,"cmdoption-osxphotos-export-place","--place"],[0,6,1,"cmdoption-osxphotos-export-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-export-post-command","--post-command"],[0,6,1,"cmdoption-osxphotos-export-post-function","--post-function"],[0,6,1,"cmdoption-osxphotos-export-preview","--preview"],[0,6,1,"cmdoption-osxphotos-export-preview-if-missing","--preview-if-missing"],[0,6,1,"cmdoption-osxphotos-export-preview-suffix","--preview-suffix"],[0,6,1,"cmdoption-osxphotos-export-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-export-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-export-ramdb","--ramdb"],[0,6,1,"cmdoption-osxphotos-export-regex","--regex"],[0,6,1,"cmdoption-osxphotos-export-replace-keywords","--replace-keywords"],[0,6,1,"cmdoption-osxphotos-export-report","--report"],[0,6,1,"cmdoption-osxphotos-export-retry","--retry"],[0,6,1,"cmdoption-osxphotos-export-save-config","--save-config"],[0,6,1,"cmdoption-osxphotos-export-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-export-selected","--selected"],[0,6,1,"cmdoption-osxphotos-export-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-export-shared","--shared"],[0,6,1,"cmdoption-osxphotos-export-sidecar","--sidecar"],[0,6,1,"cmdoption-osxphotos-export-sidecar-drop-ext","--sidecar-drop-ext"],[0,6,1,"cmdoption-osxphotos-export-skip-bursts","--skip-bursts"],[0,6,1,"cmdoption-osxphotos-export-skip-edited","--skip-edited"],[0,6,1,"cmdoption-osxphotos-export-skip-live","--skip-live"],[0,6,1,"cmdoption-osxphotos-export-skip-original-if-edited","--skip-original-if-edited"],[0,6,1,"cmdoption-osxphotos-export-skip-raw","--skip-raw"],[0,6,1,"cmdoption-osxphotos-export-skip-uuid","--skip-uuid"],[0,6,1,"cmdoption-osxphotos-export-skip-uuid-from-file","--skip-uuid-from-file"],[0,6,1,"cmdoption-osxphotos-export-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-export-strip","--strip"],[0,6,1,"cmdoption-osxphotos-export-theme","--theme"],[0,6,1,"cmdoption-osxphotos-export-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-export-timestamp","--timestamp"],[0,6,1,"cmdoption-osxphotos-export-title","--title"],[0,6,1,"cmdoption-osxphotos-export-tmpdir","--tmpdir"],[0,6,1,"cmdoption-osxphotos-export-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-export-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-export-touch-file","--touch-file"],[0,6,1,"cmdoption-osxphotos-export-update","--update"],[0,6,1,"cmdoption-osxphotos-export-use-photokit","--use-photokit"],[0,6,1,"cmdoption-osxphotos-export-use-photos-export","--use-photos-export"],[0,6,1,"cmdoption-osxphotos-export-uti","--uti"],[0,6,1,"cmdoption-osxphotos-export-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-export-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-export-V","--verbose"],[0,6,1,"cmdoption-osxphotos-export-xattr-template","--xattr-template"],[0,6,1,"cmdoption-osxphotos-export-year","--year"],[0,6,1,"cmdoption-osxphotos-export-V","-V"],[0,6,1,"cmdoption-osxphotos-export-i","-i"],[0,6,1,"cmdoption-osxphotos-export-arg-DEST","DEST"],[0,6,1,"cmdoption-osxphotos-export-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-help":[[0,6,1,"cmdoption-osxphotos-help-arg-SUBTOPIC","SUBTOPIC"],[0,6,1,"cmdoption-osxphotos-help-arg-TOPIC","TOPIC"]],"osxphotos-info":[[0,6,1,"cmdoption-osxphotos-info-db","--db"],[0,6,1,"cmdoption-osxphotos-info-json","--json"],[0,6,1,"cmdoption-osxphotos-info-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-install":[[0,6,1,"cmdoption-osxphotos-install-U","--upgrade"],[0,6,1,"cmdoption-osxphotos-install-U","-U"],[0,6,1,"cmdoption-osxphotos-install-arg-PACKAGES","PACKAGES"]],"osxphotos-keywords":[[0,6,1,"cmdoption-osxphotos-keywords-db","--db"],[0,6,1,"cmdoption-osxphotos-keywords-json","--json"],[0,6,1,"cmdoption-osxphotos-keywords-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-labels":[[0,6,1,"cmdoption-osxphotos-labels-db","--db"],[0,6,1,"cmdoption-osxphotos-labels-json","--json"],[0,6,1,"cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-list":[[0,6,1,"cmdoption-osxphotos-list-json","--json"]],"osxphotos-persons":[[0,6,1,"cmdoption-osxphotos-persons-db","--db"],[0,6,1,"cmdoption-osxphotos-persons-json","--json"],[0,6,1,"cmdoption-osxphotos-persons-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-places":[[0,6,1,"cmdoption-osxphotos-places-db","--db"],[0,6,1,"cmdoption-osxphotos-places-json","--json"],[0,6,1,"cmdoption-osxphotos-places-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-query":[[0,6,1,"cmdoption-osxphotos-query-add-to-album","--add-to-album"],[0,6,1,"cmdoption-osxphotos-query-album","--album"],[0,6,1,"cmdoption-osxphotos-query-burst","--burst"],[0,6,1,"cmdoption-osxphotos-query-cloudasset","--cloudasset"],[0,6,1,"cmdoption-osxphotos-query-db","--db"],[0,6,1,"cmdoption-osxphotos-query-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-query-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-query-description","--description"],[0,6,1,"cmdoption-osxphotos-query-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-query-edited","--edited"],[0,6,1,"cmdoption-osxphotos-query-exif","--exif"],[0,6,1,"cmdoption-osxphotos-query-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-query-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-query-folder","--folder"],[0,6,1,"cmdoption-osxphotos-query-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-query-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-query-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-query-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-query-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-query-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-query-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-query-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-query-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-query-incloud","--incloud"],[0,6,1,"cmdoption-osxphotos-query-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-query-json","--json"],[0,6,1,"cmdoption-osxphotos-query-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-query-label","--label"],[0,6,1,"cmdoption-osxphotos-query-live","--live"],[0,6,1,"cmdoption-osxphotos-query-location","--location"],[0,6,1,"cmdoption-osxphotos-query-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-query-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-query-missing","--missing"],[0,6,1,"cmdoption-osxphotos-query-name","--name"],[0,6,1,"cmdoption-osxphotos-query-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-query-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-query-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-query-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-query-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-query-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-query-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-query-not-cloudasset","--not-cloudasset"],[0,6,1,"cmdoption-osxphotos-query-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-query-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-query-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-query-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-query-not-incloud","--not-incloud"],[0,6,1,"cmdoption-osxphotos-query-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-query-not-missing","--not-missing"],[0,6,1,"cmdoption-osxphotos-query-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-query-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-query-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-query-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-query-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-query-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-query-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-query-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-query-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-query-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-query-person","--person"],[0,6,1,"cmdoption-osxphotos-query-place","--place"],[0,6,1,"cmdoption-osxphotos-query-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-query-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-query-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-query-regex","--regex"],[0,6,1,"cmdoption-osxphotos-query-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-query-selected","--selected"],[0,6,1,"cmdoption-osxphotos-query-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-query-shared","--shared"],[0,6,1,"cmdoption-osxphotos-query-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-query-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-query-title","--title"],[0,6,1,"cmdoption-osxphotos-query-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-query-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-query-uti","--uti"],[0,6,1,"cmdoption-osxphotos-query-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-query-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-query-year","--year"],[0,6,1,"cmdoption-osxphotos-query-i","-i"],[0,6,1,"cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY","PHOTOS_LIBRARY"]],"osxphotos-repl":[[0,6,1,"cmdoption-osxphotos-repl-album","--album"],[0,6,1,"cmdoption-osxphotos-repl-burst","--burst"],[0,6,1,"cmdoption-osxphotos-repl-cloudasset","--cloudasset"],[0,6,1,"cmdoption-osxphotos-repl-db","--db"],[0,6,1,"cmdoption-osxphotos-repl-deleted","--deleted"],[0,6,1,"cmdoption-osxphotos-repl-deleted-only","--deleted-only"],[0,6,1,"cmdoption-osxphotos-repl-description","--description"],[0,6,1,"cmdoption-osxphotos-repl-duplicate","--duplicate"],[0,6,1,"cmdoption-osxphotos-repl-edited","--edited"],[0,6,1,"cmdoption-osxphotos-repl-emacs","--emacs"],[0,6,1,"cmdoption-osxphotos-repl-exif","--exif"],[0,6,1,"cmdoption-osxphotos-repl-external-edit","--external-edit"],[0,6,1,"cmdoption-osxphotos-repl-favorite","--favorite"],[0,6,1,"cmdoption-osxphotos-repl-folder","--folder"],[0,6,1,"cmdoption-osxphotos-repl-from-date","--from-date"],[0,6,1,"cmdoption-osxphotos-repl-from-time","--from-time"],[0,6,1,"cmdoption-osxphotos-repl-has-comment","--has-comment"],[0,6,1,"cmdoption-osxphotos-repl-has-likes","--has-likes"],[0,6,1,"cmdoption-osxphotos-repl-has-raw","--has-raw"],[0,6,1,"cmdoption-osxphotos-repl-hdr","--hdr"],[0,6,1,"cmdoption-osxphotos-repl-hidden","--hidden"],[0,6,1,"cmdoption-osxphotos-repl-i","--ignore-case"],[0,6,1,"cmdoption-osxphotos-repl-in-album","--in-album"],[0,6,1,"cmdoption-osxphotos-repl-incloud","--incloud"],[0,6,1,"cmdoption-osxphotos-repl-is-reference","--is-reference"],[0,6,1,"cmdoption-osxphotos-repl-keyword","--keyword"],[0,6,1,"cmdoption-osxphotos-repl-label","--label"],[0,6,1,"cmdoption-osxphotos-repl-live","--live"],[0,6,1,"cmdoption-osxphotos-repl-location","--location"],[0,6,1,"cmdoption-osxphotos-repl-max-size","--max-size"],[0,6,1,"cmdoption-osxphotos-repl-min-size","--min-size"],[0,6,1,"cmdoption-osxphotos-repl-missing","--missing"],[0,6,1,"cmdoption-osxphotos-repl-name","--name"],[0,6,1,"cmdoption-osxphotos-repl-no-comment","--no-comment"],[0,6,1,"cmdoption-osxphotos-repl-no-description","--no-description"],[0,6,1,"cmdoption-osxphotos-repl-no-likes","--no-likes"],[0,6,1,"cmdoption-osxphotos-repl-no-location","--no-location"],[0,6,1,"cmdoption-osxphotos-repl-no-place","--no-place"],[0,6,1,"cmdoption-osxphotos-repl-no-title","--no-title"],[0,6,1,"cmdoption-osxphotos-repl-not-burst","--not-burst"],[0,6,1,"cmdoption-osxphotos-repl-not-cloudasset","--not-cloudasset"],[0,6,1,"cmdoption-osxphotos-repl-not-favorite","--not-favorite"],[0,6,1,"cmdoption-osxphotos-repl-not-hdr","--not-hdr"],[0,6,1,"cmdoption-osxphotos-repl-not-hidden","--not-hidden"],[0,6,1,"cmdoption-osxphotos-repl-not-in-album","--not-in-album"],[0,6,1,"cmdoption-osxphotos-repl-not-incloud","--not-incloud"],[0,6,1,"cmdoption-osxphotos-repl-not-live","--not-live"],[0,6,1,"cmdoption-osxphotos-repl-not-missing","--not-missing"],[0,6,1,"cmdoption-osxphotos-repl-not-panorama","--not-panorama"],[0,6,1,"cmdoption-osxphotos-repl-not-portrait","--not-portrait"],[0,6,1,"cmdoption-osxphotos-repl-not-screenshot","--not-screenshot"],[0,6,1,"cmdoption-osxphotos-repl-not-selfie","--not-selfie"],[0,6,1,"cmdoption-osxphotos-repl-not-shared","--not-shared"],[0,6,1,"cmdoption-osxphotos-repl-not-slow-mo","--not-slow-mo"],[0,6,1,"cmdoption-osxphotos-repl-not-time-lapse","--not-time-lapse"],[0,6,1,"cmdoption-osxphotos-repl-only-movies","--only-movies"],[0,6,1,"cmdoption-osxphotos-repl-only-photos","--only-photos"],[0,6,1,"cmdoption-osxphotos-repl-panorama","--panorama"],[0,6,1,"cmdoption-osxphotos-repl-person","--person"],[0,6,1,"cmdoption-osxphotos-repl-place","--place"],[0,6,1,"cmdoption-osxphotos-repl-portrait","--portrait"],[0,6,1,"cmdoption-osxphotos-repl-query-eval","--query-eval"],[0,6,1,"cmdoption-osxphotos-repl-query-function","--query-function"],[0,6,1,"cmdoption-osxphotos-repl-regex","--regex"],[0,6,1,"cmdoption-osxphotos-repl-screenshot","--screenshot"],[0,6,1,"cmdoption-osxphotos-repl-selected","--selected"],[0,6,1,"cmdoption-osxphotos-repl-selfie","--selfie"],[0,6,1,"cmdoption-osxphotos-repl-shared","--shared"],[0,6,1,"cmdoption-osxphotos-repl-slow-mo","--slow-mo"],[0,6,1,"cmdoption-osxphotos-repl-time-lapse","--time-lapse"],[0,6,1,"cmdoption-osxphotos-repl-title","--title"],[0,6,1,"cmdoption-osxphotos-repl-to-date","--to-date"],[0,6,1,"cmdoption-osxphotos-repl-to-time","--to-time"],[0,6,1,"cmdoption-osxphotos-repl-uti","--uti"],[0,6,1,"cmdoption-osxphotos-repl-uuid","--uuid"],[0,6,1,"cmdoption-osxphotos-repl-uuid-from-file","--uuid-from-file"],[0,6,1,"cmdoption-osxphotos-repl-year","--year"],[0,6,1,"cmdoption-osxphotos-repl-i","-i"]],"osxphotos-run":[[0,6,1,"cmdoption-osxphotos-run-arg-PYTHON_FILE","PYTHON_FILE"]],"osxphotos-snap":[[0,6,1,"cmdoption-osxphotos-snap-db","--db"]],"osxphotos-theme":[[0,6,1,"cmdoption-osxphotos-theme-clone","--clone"],[0,6,1,"cmdoption-osxphotos-theme-config","--config"],[0,6,1,"cmdoption-osxphotos-theme-default","--default"],[0,6,1,"cmdoption-osxphotos-theme-delete","--delete"],[0,6,1,"cmdoption-osxphotos-theme-edit","--edit"],[0,6,1,"cmdoption-osxphotos-theme-list","--list"],[0,6,1,"cmdoption-osxphotos-theme-preview","--preview"]],"osxphotos-timewarp":[[0,6,1,"cmdoption-osxphotos-timewarp-a","--add-to-album"],[0,6,1,"cmdoption-osxphotos-timewarp-c","--compare-exif"],[0,6,1,"cmdoption-osxphotos-timewarp-d","--date"],[0,6,1,"cmdoption-osxphotos-timewarp-D","--date-delta"],[0,6,1,"cmdoption-osxphotos-timewarp-e","--exiftool-path"],[0,6,1,"cmdoption-osxphotos-timewarp-i","--inspect"],[0,6,1,"cmdoption-osxphotos-timewarp-L","--library"],[0,6,1,"cmdoption-osxphotos-timewarp-m","--match-time"],[0,6,1,"cmdoption-osxphotos-timewarp-o","--output-file"],[0,6,1,"cmdoption-osxphotos-timewarp-plain","--plain"],[0,6,1,"cmdoption-osxphotos-timewarp-P","--pull-exif"],[0,6,1,"cmdoption-osxphotos-timewarp-p","--push-exif"],[0,6,1,"cmdoption-osxphotos-timewarp-theme","--theme"],[0,6,1,"cmdoption-osxphotos-timewarp-t","--time"],[0,6,1,"cmdoption-osxphotos-timewarp-T","--time-delta"],[0,6,1,"cmdoption-osxphotos-timewarp-timestamp","--timestamp"],[0,6,1,"cmdoption-osxphotos-timewarp-z","--timezone"],[0,6,1,"cmdoption-osxphotos-timewarp-f","--use-file-time"],[0,6,1,"cmdoption-osxphotos-timewarp-V","--verbose"],[0,6,1,"cmdoption-osxphotos-timewarp-D","-D"],[0,6,1,"cmdoption-osxphotos-timewarp-L","-L"],[0,6,1,"cmdoption-osxphotos-timewarp-P","-P"],[0,6,1,"cmdoption-osxphotos-timewarp-T","-T"],[0,6,1,"cmdoption-osxphotos-timewarp-V","-V"],[0,6,1,"cmdoption-osxphotos-timewarp-a","-a"],[0,6,1,"cmdoption-osxphotos-timewarp-c","-c"],[0,6,1,"cmdoption-osxphotos-timewarp-d","-d"],[0,6,1,"cmdoption-osxphotos-timewarp-e","-e"],[0,6,1,"cmdoption-osxphotos-timewarp-f","-f"],[0,6,1,"cmdoption-osxphotos-timewarp-i","-i"],[0,6,1,"cmdoption-osxphotos-timewarp-m","-m"],[0,6,1,"cmdoption-osxphotos-timewarp-o","-o"],[0,6,1,"cmdoption-osxphotos-timewarp-p","-p"],[0,6,1,"cmdoption-osxphotos-timewarp-t","-t"],[0,6,1,"cmdoption-osxphotos-timewarp-z","-z"]],"osxphotos-tutorial":[[0,6,1,"cmdoption-osxphotos-tutorial-arg-WIDTH","WIDTH"]],"osxphotos-uninstall":[[0,6,1,"cmdoption-osxphotos-uninstall-y","--yes"],[0,6,1,"cmdoption-osxphotos-uninstall-y","-y"],[0,6,1,"cmdoption-osxphotos-uninstall-arg-PACKAGES","PACKAGES"]],"osxphotos-uuid":[[0,6,1,"cmdoption-osxphotos-uuid-f","--filename"],[0,6,1,"cmdoption-osxphotos-uuid-f","-f"]],"osxphotos-version":[[0,6,1,"cmdoption-osxphotos-version-run","--run"]],"osxphotos.AlbumInfo":[[4,2,1,"","folder_list"],[4,2,1,"","folder_names"],[4,2,1,"","parent"],[4,3,1,"","photo_index"],[4,2,1,"","photos"],[4,2,1,"","sort_order"],[4,2,1,"","title"]],"osxphotos.ExifTool":[[4,3,1,"","addvalues"],[4,3,1,"","asdict"],[4,3,1,"","json"],[4,2,1,"","pid"],[4,3,1,"","run_commands"],[4,3,1,"","setvalue"],[4,2,1,"","version"]],"osxphotos.ExportDB":[[4,3,1,"","close"],[4,3,1,"","create_file_record"],[4,3,1,"","create_or_get_file_record"],[4,2,1,"","export_dir"],[4,3,1,"","get_file_record"],[4,3,1,"","get_photoinfo_for_uuid"],[4,3,1,"","get_previous_uuids"],[4,3,1,"","get_uuid_for_file"],[4,2,1,"","path"],[4,3,1,"","set_config"],[4,3,1,"","set_photoinfo_for_uuid"]],"osxphotos.ExportOptions":[[4,2,1,"","bit_flags"],[4,4,1,"","convert_to_jpeg"],[4,4,1,"","description_template"],[4,4,1,"","download_missing"],[4,4,1,"","dry_run"],[4,4,1,"","edited"],[4,4,1,"","exiftool"],[4,4,1,"","exiftool_flags"],[4,4,1,"","export_as_hardlink"],[4,4,1,"","export_db"],[4,4,1,"","face_regions"],[4,4,1,"","fileutil"],[4,4,1,"","force_update"],[4,4,1,"","ignore_date_modified"],[4,4,1,"","ignore_signature"],[4,4,1,"","increment"],[4,4,1,"","jpeg_ext"],[4,4,1,"","jpeg_quality"],[4,4,1,"","keyword_template"],[4,4,1,"","live_photo"],[4,4,1,"","location"],[4,4,1,"","merge_exif_keywords"],[4,4,1,"","merge_exif_persons"],[4,4,1,"","overwrite"],[4,4,1,"","persons"],[4,4,1,"","preview"],[4,4,1,"","preview_suffix"],[4,4,1,"","raw_photo"],[4,4,1,"","render_options"],[4,4,1,"","replace_keywords"],[4,4,1,"","rich"],[4,4,1,"","sidecar"],[4,4,1,"","sidecar_drop_ext"],[4,4,1,"","strip"],[4,4,1,"","timeout"],[4,4,1,"","tmpdir"],[4,4,1,"","touch_file"],[4,4,1,"","update"],[4,4,1,"","use_albums_as_keywords"],[4,4,1,"","use_persons_as_keywords"],[4,4,1,"","use_photokit"],[4,4,1,"","use_photos_export"],[4,4,1,"","verbose"]],"osxphotos.ExportResults":[[4,3,1,"","all_files"]],"osxphotos.FileUtilNoOp":[[4,3,1,"","convert_to_jpeg"],[4,3,1,"","copy"],[4,3,1,"","file_sig"],[4,3,1,"","hardlink"],[4,3,1,"","rename"],[4,3,1,"","rmdir"],[4,3,1,"","tmpdir"],[4,3,1,"","unlink"],[4,3,1,"","utime"]],"osxphotos.FolderInfo":[[4,2,1,"","album_info"],[4,2,1,"","parent"],[4,2,1,"","subfolders"],[4,2,1,"","title"],[4,2,1,"","uuid"]],"osxphotos.ImportInfo":[[4,2,1,"","photos"]],"osxphotos.MomentInfo":[[4,2,1,"","date"],[4,2,1,"","end_date"],[4,2,1,"","location"],[4,2,1,"","modification_date"],[4,2,1,"","photos"],[4,2,1,"","pk"],[4,2,1,"","start_date"],[4,2,1,"","subtitle"],[4,2,1,"","title"]],"osxphotos.PersonInfo":[[4,3,1,"","asdict"],[4,2,1,"","face_info"],[4,3,1,"","json"],[4,2,1,"","photos"]],"osxphotos.PhotoExporter":[[4,3,1,"","export"]],"osxphotos.PhotoInfo":[[4,2,1,"","adjustments"],[4,2,1,"","album_info"],[4,2,1,"","albums"],[4,3,1,"","asdict"],[4,2,1,"","burst"],[4,2,1,"","burst_album_info"],[4,2,1,"","burst_albums"],[4,2,1,"","burst_default_pick"],[4,2,1,"","burst_key"],[4,2,1,"","burst_photos"],[4,2,1,"","burst_selected"],[4,2,1,"","comments"],[4,2,1,"","date"],[4,2,1,"","date_added"],[4,2,1,"","date_modified"],[4,2,1,"","date_trashed"],[4,2,1,"","description"],[4,3,1,"","detected_text"],[4,2,1,"","duplicates"],[4,2,1,"","exif_info"],[4,2,1,"","exiftool"],[4,3,1,"","export"],[4,2,1,"","external_edit"],[4,2,1,"","face_info"],[4,2,1,"","favorite"],[4,2,1,"","filename"],[4,2,1,"","has_raw"],[4,2,1,"","hasadjustments"],[4,2,1,"","hdr"],[4,2,1,"","height"],[4,2,1,"","hidden"],[4,2,1,"","import_info"],[4,2,1,"","incloud"],[4,2,1,"","intrash"],[4,2,1,"","iscloudasset"],[4,2,1,"","ismissing"],[4,2,1,"","ismovie"],[4,2,1,"","isphoto"],[4,2,1,"","israw"],[4,2,1,"","isreference"],[4,3,1,"","json"],[4,2,1,"","keywords"],[4,2,1,"","labels"],[4,2,1,"","labels_normalized"],[4,2,1,"","likes"],[4,2,1,"","live_photo"],[4,2,1,"","location"],[4,2,1,"","moment"],[4,2,1,"","orientation"],[4,2,1,"","original_filename"],[4,2,1,"","original_filesize"],[4,2,1,"","original_height"],[4,2,1,"","original_orientation"],[4,2,1,"","original_width"],[4,2,1,"","owner"],[4,2,1,"","panorama"],[4,2,1,"","path"],[4,2,1,"","path_derivatives"],[4,2,1,"","path_edited"],[4,2,1,"","path_edited_live_photo"],[4,2,1,"","path_live_photo"],[4,2,1,"","path_raw"],[4,2,1,"","person_info"],[4,2,1,"","persons"],[4,2,1,"","place"],[4,2,1,"","portrait"],[4,2,1,"","project_info"],[4,2,1,"","raw_original"],[4,3,1,"","render_template"],[4,2,1,"","score"],[4,2,1,"","screenshot"],[4,2,1,"","search_info"],[4,2,1,"","search_info_normalized"],[4,2,1,"","selfie"],[4,2,1,"","shared"],[4,2,1,"","slow_mo"],[4,2,1,"","time_lapse"],[4,2,1,"","title"],[4,2,1,"","tzoffset"],[4,2,1,"","uti"],[4,2,1,"","uti_edited"],[4,2,1,"","uti_original"],[4,2,1,"","uti_raw"],[4,2,1,"","uuid"],[4,2,1,"","visible"],[4,2,1,"","width"]],"osxphotos.PhotoTemplate":[[4,3,1,"","get_media_type"],[4,3,1,"","get_photo_video_type"],[4,3,1,"","get_template_value"],[4,3,1,"","get_template_value_exiftool"],[4,3,1,"","get_template_value_filter_function"],[4,3,1,"","get_template_value_function"],[4,3,1,"","get_template_value_multi"],[4,3,1,"","get_template_value_pathlib"],[4,3,1,"","render"]],"osxphotos.PhotosDB":[[4,2,1,"","album_info"],[4,2,1,"","album_info_shared"],[4,2,1,"","albums"],[4,2,1,"","albums_as_dict"],[4,2,1,"","albums_shared"],[4,2,1,"","albums_shared_as_dict"],[4,2,1,"","db_path"],[4,2,1,"","db_version"],[4,3,1,"","execute"],[4,2,1,"","folder_info"],[4,2,1,"","folders"],[4,3,1,"","get_db_connection"],[4,3,1,"","get_photo"],[4,2,1,"","import_info"],[4,2,1,"","keywords"],[4,2,1,"","keywords_as_dict"],[4,2,1,"","labels"],[4,2,1,"","labels_as_dict"],[4,2,1,"","labels_normalized"],[4,2,1,"","labels_normalized_as_dict"],[4,2,1,"","library_path"],[4,2,1,"","person_info"],[4,2,1,"","persons"],[4,2,1,"","persons_as_dict"],[4,3,1,"","photos"],[4,3,1,"","photos_by_uuid"],[4,2,1,"","project_info"],[4,3,1,"","query"]],"osxphotos.QueryOptions":[[4,4,1,"","album"],[4,4,1,"","burst"],[4,4,1,"","burst_photos"],[4,4,1,"","cloudasset"],[4,4,1,"","deleted"],[4,4,1,"","deleted_only"],[4,4,1,"","description"],[4,4,1,"","duplicate"],[4,4,1,"","edited"],[4,4,1,"","exif"],[4,4,1,"","external_edit"],[4,4,1,"","favorite"],[4,4,1,"","folder"],[4,4,1,"","from_date"],[4,4,1,"","function"],[4,4,1,"","has_comment"],[4,4,1,"","has_likes"],[4,4,1,"","has_raw"],[4,4,1,"","hdr"],[4,4,1,"","hidden"],[4,4,1,"","ignore_case"],[4,4,1,"","in_album"],[4,4,1,"","incloud"],[4,4,1,"","is_reference"],[4,4,1,"","keyword"],[4,4,1,"","label"],[4,4,1,"","live"],[4,4,1,"","location"],[4,4,1,"","max_size"],[4,4,1,"","min_size"],[4,4,1,"","missing"],[4,4,1,"","missing_bursts"],[4,4,1,"","movies"],[4,4,1,"","name"],[4,4,1,"","no_comment"],[4,4,1,"","no_description"],[4,4,1,"","no_likes"],[4,4,1,"","no_location"],[4,4,1,"","no_place"],[4,4,1,"","no_title"],[4,4,1,"","not_burst"],[4,4,1,"","not_cloudasset"],[4,4,1,"","not_favorite"],[4,4,1,"","not_hdr"],[4,4,1,"","not_hidden"],[4,4,1,"","not_in_album"],[4,4,1,"","not_incloud"],[4,4,1,"","not_live"],[4,4,1,"","not_missing"],[4,4,1,"","not_panorama"],[4,4,1,"","not_portrait"],[4,4,1,"","not_screenshot"],[4,4,1,"","not_selfie"],[4,4,1,"","not_shared"],[4,4,1,"","not_slow_mo"],[4,4,1,"","not_time_lapse"],[4,4,1,"","panorama"],[4,4,1,"","person"],[4,4,1,"","photos"],[4,4,1,"","place"],[4,4,1,"","portrait"],[4,4,1,"","query_eval"],[4,4,1,"","regex"],[4,4,1,"","screenshot"],[4,4,1,"","selected"],[4,4,1,"","selfie"],[4,4,1,"","shared"],[4,4,1,"","slow_mo"],[4,4,1,"","time_lapse"],[4,4,1,"","title"],[4,4,1,"","to_date"],[4,4,1,"","uti"],[4,4,1,"","uuid"],[4,4,1,"","year"]],"osxphotos.SearchInfo":[[4,2,1,"","activities"],[4,2,1,"","all"],[4,3,1,"","asdict"],[4,2,1,"","bodies_of_water"],[4,2,1,"","city"],[4,2,1,"","country"],[4,2,1,"","holidays"],[4,2,1,"","labels"],[4,2,1,"","locality_names"],[4,2,1,"","media_types"],[4,2,1,"","month"],[4,2,1,"","neighborhoods"],[4,2,1,"","place_names"],[4,2,1,"","season"],[4,2,1,"","state"],[4,2,1,"","state_abbreviation"],[4,2,1,"","streets"],[4,2,1,"","venue_types"],[4,2,1,"","venues"],[4,2,1,"","year"]],osxphotos:[[4,1,1,"","AlbumInfo"],[4,1,1,"","AlbumSortOrder"],[4,1,1,"","CommentInfo"],[4,1,1,"","ExifInfo"],[4,1,1,"","ExifTool"],[4,1,1,"","ExportDB"],[4,1,1,"","ExportOptions"],[4,1,1,"","ExportResults"],[4,1,1,"","FileUtil"],[4,1,1,"","FileUtilNoOp"],[4,1,1,"","FolderInfo"],[4,1,1,"","ImportInfo"],[4,1,1,"","LikeInfo"],[4,1,1,"","MomentInfo"],[4,1,1,"","PersonInfo"],[4,1,1,"","PhotoExporter"],[4,1,1,"","PhotoInfo"],[4,1,1,"","PhotoTemplate"],[4,1,1,"","PhotosDB"],[4,1,1,"","PlaceInfo"],[4,1,1,"","ProjectInfo"],[4,1,1,"","QueryOptions"],[4,1,1,"","ScoreInfo"],[4,1,1,"","SearchInfo"],[4,5,1,"","is_debug"],[4,5,1,"","set_debug"],[0,6,1,"cmdoption-osxphotos-db","--db"],[0,6,1,"cmdoption-osxphotos-json","--json"],[0,6,1,"cmdoption-osxphotos-v","--version"],[0,6,1,"cmdoption-osxphotos-v","-v"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","property","Python property"],"3":["py","method","Python method"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["std","cmdoption","program option"]},objtypes:{"0":"py:module","1":"py:class","2":"py:property","3":"py:method","4":"py:attribute","5":"py:function","6":"std:cmdoption"},terms:{"0":[0,2,3,4,5,6],"00":0,"00000":5,"00001":5,"00002":5,"00003":5,"01":[0,3,6],"02":[0,5,6],"03":5,"04":[5,6],"048576mb":0,"05":6,"05d":5,"07":[0,3,5],"0b16":5,"1":[0,2,4,5,6],"10":[0,2,3,5],"100":6,"1048576":0,"11":[],"12":[0,2,3],"120":4,"1200":3,"128fb4c6":5,"12t12":0,"13":[0,5],"15":[0,2,5],"15mm":5,"18":3,"18th":5,"19":5,"190738":5,"1gb":[],"2":[0,2,3,4,5],"20":0,"2000":0,"20009":5,"2001":0,"2007":5,"2015":6,"2018":6,"2019":[0,6],"2020":[5,6],"2021":6,"2022":[0,3],"21":6,"22":5,"23":5,"28":6,"29":3,"3":[0,2,3,5,6],"31":[0,6],"31581":3,"32":3,"36":[3,5],"38":5,"4":[4,5,6],"41cd":3,"47":[3,5],"4e7d":5,"5":[0,2,3,4,5],"50":6,"54":3,"6":2,"6000":3,"6s":5,"7":5,"75":[4,5],"77":3,"8":2,"8601":0,"9":6,"9108":5,"913095d62e31":3,"94e0":3,"boolean":[4,5,6],"byte":[0,4],"case":[0,4,5,6],"class":[0,3,4,5],"default":[0,3,4,5,6],"do":[0,2,3,4,5,6],"export":[1,2,3,4,5],"final":6,"float":4,"function":[0,3,4,5],"import":[0,3,4,5,6],"int":4,"long":4,"new":[0,2,4,6],"null":[5,6],"public":[4,5],"return":[0,3,4,5],"short":6,"true":[3,4,5,6],"try":6,"vid\u00e9o":5,"vid\u00e9o_acc\u00e9l\u00e9r\u00e9":5,"while":[0,6],A:[0,5,6],AND:[0,4],And:6,As:6,Be:0,By:[0,6],For:[0,4,5,6],If:[0,2,3,4,5,6],In:[0,4,5,6],Is:6,It:[0,4,5,6],No:[4,6],Not:5,OR:[0,4],Of:6,On:4,One:6,That:[0,6],The:[0,2,3,4,5,6],Then:[2,6],There:6,These:[0,3,5,6],To:[0,2,4,5,6],With:6,_:[5,6],__main__:3,__name__:3,_bearbeiten:0,_edit:[0,6],_low_r:0,_origin:0,_preview:[0,4],abbrevi:[4,5],abil:2,abl:[0,2,6],about:[1,2,3,4,6],abov:[5,6],absolut:4,access:[4,5,6],accord:2,activ:[4,5],actual:[0,1,4,5],ad:[0,4,5,6],add:[0,4,5,6],addit:[0,3,4,5,6],address:5,addvalu:4,adjust:[0,2,4,5,6],adjustmentsinfo:4,adob:0,advanc:6,advantag:0,aesthet:[3,5],af71:3,affect:4,after:[0,4,5,6],again:6,against:[4,6],ahead:[0,4],aka:2,album1:5,album:[1,2,3,4,5,6],album_info:4,album_info_shar:4,album_nam:3,album_project:5,album_seq:5,albuminfo:[3,4],albums_as_dict:[3,4],albums_shar:4,albums_shared_as_dict:4,albumsortord:4,algorithm:[5,6],alia:5,alic:3,all:[0,1,2,3,4,5],all_fil:4,all_photo:3,allow:[0,2,5,6],almost:6,alpha:0,alreadi:[0,4,6],also:[0,2,3,4,5,6],altern:[0,6],alwai:6,amount:6,an:[0,1,3,4,5],ani:[0,2,3,4,5,6],anoth:[5,6],anyth:6,apertur:4,apf:0,api:1,app:[0,2,4,6],appear:4,append:[0,4,5,6],appl:[0,2,4,5,6],applescript:[0,4,6],appli:[0,4,5,6],applic:[2,6],april:6,ar:[0,3,4,5,6],area:5,area_of_interest:5,aren:4,arg:[0,2,4],argument:[0,2,3,6],asdict:4,ask:0,asset:[4,5,6],assign:[5,6],associ:[0,2,4,5,6],assum:[4,5],asynchroun:4,attach:6,attempt:[0,2,4,6],attribut:[0,1],author:[0,6],auto:3,automat:[0,5,6],avail:[0,4,5,6],awar:4,b13f4485:3,back:[4,5,6],backup:6,bar:[0,3,5],bare:5,base:[3,4,5,6],basi:6,basic:[4,6],beach:[0,5],beachdai:5,becaus:6,been:[0,3,4,5,6],befor:[0,4,5,6],begin:[0,5],behavior:[0,4],being:[0,4,5,6],belong:4,below:[0,4],best:[0,4,6],beta:[],better:6,between:[0,4,5,6],bin:6,bind:0,bit:4,bit_flag:4,bit_rat:4,bitmath:4,blank:6,blob:[0,5],bodi:4,bodies_of_wat:4,bool:4,bool_val:4,bool_valu:5,boost:6,both:[0,2,5,6],brace:[5,6],bracket:5,brew:[0,2],browser:[0,2],buggi:6,built:5,burst:[0,4,5],burst_album:4,burst_album_info:4,burst_default_pick:4,burst_kei:4,burst_photo:4,burst_select:4,c:0,cach:5,calendar:[4,5],call:[0,2,4,5,6],callabl:4,caller:4,camera:[0,4,5],camera_mak:[4,5],camera_model:[4,5],can:[0,2,3,4,5,6],cannot:[0,4,5],canon:[0,4],capabl:6,capit:5,caption:6,card:[4,5],care:[],carri:6,carriag:5,catalina:[0,5],categor:[4,5],categori:[0,6],caus:[5,6],caution:0,cd:[],certain:[0,1,5],certainli:6,chang:[0,4,5,6],charact:[3,5,6],check:[0,2],chose:6,christma:5,citi:[4,5],clang:3,classic:6,classif:[0,2,6],classifi:6,classmethod:4,cleanup:0,cli:[1,2],click:3,clone:0,close:[4,5],closebrac:5,closebracket:5,closeparen:5,cloud:[4,6],cloudasset:[0,4],cmp:4,cmp_file_sig:4,code:[1,2,5],codec:4,collis:0,colon:5,color:[0,1,2],com:[0,4,5],combin:[5,6],comfort:[],comma:[5,6],command:[1,3,4,5],command_nam:2,comment:[0,3,4,5,6],commentinfo:4,common:6,commun:6,compar:[0,2,4],comparison:5,compat:[0,6],complet:[4,6],complex:[0,6],compon:0,comprehens:[0,6],compress:[0,4],compression_qu:4,comput:[3,4],concept:6,conclus:1,condit:[5,6],confid:[4,5],confidence_threshold:4,config:[0,4,6],config_data:4,configur:[0,6],confirm:0,conform:4,conjunct:[0,6],connect:[0,4,6],consist:0,contact:[],contain:[0,3,4,5,6],content:4,context:[0,4],contribut:6,convert:[0,1,4,5],convert_to_jpeg:4,converted_to_jpeg:[0,4],coordin:[0,6],copi:[0,3,4,6],copyright:[0,6],correct:[0,4,6],correspond:0,could:[0,3,5,6],count:[4,5],counterintuit:0,countri:[4,5,6],country_cod:5,cours:6,cover:6,cr2:4,cr:5,crash:[0,6],creat:[0,1,2,3,4,5],create_file_record:4,create_or_get_file_record:4,created:0,creation:[4,5,6],creationd:0,creator:0,criteria:[0,4,6],crlf:5,crop:6,csv:[0,6],curat:4,curli:[5,6],current:[0,4,5,6],cursor:4,custom:[3,5,6],cut:6,d:[0,5],dai:[0,5,6],dark:[0,6],data:[0,4,5,6],databas:[0,2,3,4,5,6],date:[0,1,2,3,4,5],date_ad:4,date_modifi:4,date_trash:4,datecr:0,datetim:4,datetimeorigin:[0,4],daunt:6,db2:0,db:[0,2,3,4,6],db_path:4,db_version:4,dbfile:4,dbpath:6,dc:5,dd:[0,5,6],debug:[0,2,4],decid:6,decreas:6,def:3,default_album:3,defin:[0,3,5,6],degre:4,delai:4,delet:[0,4],deleted_directori:4,deleted_fil:4,deleted_onli:4,delim:[4,5],delimit:[4,5],delta:0,demonstr:6,depend:[0,5],deriv:4,desc:0,descend:4,descr:[0,5,6],describ:6,descript:[0,2,3,4,5,6],description_templ:4,design:6,desir:6,desktop:6,dest:[0,4],dest_dir:3,dest_fil:4,destin:[3,4],destruct:4,detail:[0,3,4,6],detect:[3,4,5],detected_text:[4,5],determin:[4,6],develop:3,devic:6,dict:4,dictionari:4,did:[0,6],diff:[1,2],differ:[0,2,4,6],digikam:[0,6],digit:[5,6],dir:[0,3,4],direct:[0,5],directli:[0,2,6],directori:[0,1,3,4,5],dirpath:4,disabl:[],discuss:6,disk:[0,1,4],displai:[0,2,5],distinguish:6,dng:6,doc:[1,2],document:[0,2,5],doe:[0,3,4,5,6],doesn:[0,6],doi:5,don:[0,6],done:[3,6],dot:6,dow:5,down:[5,6],download:[0,4,6],download_miss:4,drive:[0,6],drop:[0,4,6],dry:[0,1,4],dry_run:4,dsc05678:6,dump:[1,2],duplic:[0,3,4,5],durat:4,dure:[0,4,6],dynam:0,e:[0,2,3,4,5,6],each:[0,4,5,6],earlier:4,easi:6,easiest:2,easili:[2,6],echo:[0,3,6],edit:[0,1,2,3,4,5],edited_filenam:[],edited_nam:3,edited_vers:5,editor:[0,4],either:[0,2,4,5,6],els:[3,4,6],emac:0,embed:[0,6],empti:[4,5,6],en:[],enabl:6,enclos:5,encount:6,end:[0,4,5],end_dat:4,endswith:5,ensur:[4,6],entir:[0,4,6],environ:[0,2,3],equal:5,equival:0,error:[0,4,6],error_str:[],errror:4,etc:[0,2,4,5,6],eval:0,evalu:[0,3,4,5,6],even:[0,4,6],event:5,everi:6,everyth:6,exact:4,exactli:[5,6],exampl:[0,1,2,4,5],excel:6,except:4,execut:[0,4,5,6],exhibit:0,exif:[0,3,4,5],exif_info:4,exif_tag:0,exif_upd:[0,4],exifinfo:4,exiftool:[0,3,4,5,6],exiftool_error:4,exiftool_flag:4,exiftool_path:[0,4],exiftool_pod:0,exiftool_warn:4,exiftoolcach:4,exist:[0,3,4,6],exit:[0,2,3,4],expand:[4,5,6],expandus:3,expect:[0,6],experi:0,experiment:[0,6],explain:6,explan:6,explicitli:6,explor:[3,6],export2:[],export_as_hardlink:4,export_db:4,export_dir:[0,4,6],export_path:3,exportdb:[0,4,6],exportdb_abc:[],exportdb_fil:0,exportdbinmemori:4,exported_album:4,exportopt:[3,4],exportrecord:4,exportresult:[3,4],exposure_bia:4,express:[0,4,5,6],ext:[0,6],extend:[0,4,6],extens:[0,4,6],extern:[0,1,4,5],external_edit:4,extract:[4,5],f1:4,f:[0,3,4,5],face:[0,2,3,4,5,6],face_info:4,face_region:4,faceinfo:4,fail:4,failur:4,fall:4,fals:[4,5,6],familiar:[],faster:[0,4,6],favorit:[0,4,5,6],fb2e90da1546:5,featur:[0,3,5,6],feb:3,februari:5,feed:5,few:6,fff:0,field:[4,5,6],file1:4,file:[0,1,2,3,4,5],file_cmp:4,file_sig:4,filenam:[0,1,3,4,5],filename_origin:0,filepath:[0,3,4,6],files:4,filesystem:[4,6],fileutil:4,fileutilabc:4,fileutilnoop:4,filter:[0,3,4,5,6],filter_:4,find:[0,2,3,4,5,6],finder:[0,1],findercom:[0,6],first:[0,4,5,6],flag:[0,4],flaki:6,flash_fir:4,flexibl:[2,6],focal_length:4,focu:6,folder1:[5,6],folder1album1:5,folder2:6,folder:[0,3,4,5,6],folder_album:[0,4,5,6],folder_album_project:5,folder_album_seq:5,folder_info:4,folder_list:4,folder_nam:4,folderinfo:4,follow:[0,2,3,5,6],foo:[3,5],foobar:5,forc:[0,4,6],force_upd:4,form:[0,4,5,6],format:[0,2,4,5,6],foto:5,found:[0,2,3,4,5,6],fp:4,free:[5,6],from:[0,1,2,3,4,5],from_dat:[0,4],from_tim:[0,4],front:[0,4],full:[0,4,5,6],function_nam:5,futur:[5,6],g:[0,2,3,4,5,6],gener:[0,4,6],geoloc:[0,4,5,6],get:[0,2,3,4,5,6],get_db_connect:4,get_file_record:4,get_media_typ:4,get_photo:[3,4],get_photo_video_typ:4,get_photoinfo_for_uuid:4,get_previous_uuid:4,get_select:3,get_template_valu:4,get_template_value_exiftool:4,get_template_value_filter_funct:4,get_template_value_funct:4,get_template_value_multi:4,get_template_value_pathlib:4,get_uuid_for_fil:4,github:[0,5],give:0,given:4,gmt:0,good:6,gopro:6,gp:[0,6],gpscoordin:0,gpslatitud:0,gpslatituderef:0,gpslongitud:0,gpslongituderef:0,gpsposit:0,gpu:0,greater:5,greet:5,group:[0,4,5],h:[0,2,5],ha:[0,3,4,5,6],had:[0,6],handl:[0,6],happen:0,hard:6,hardlink:[0,4],harmonious_color:4,has_com:4,has_lik:4,has_raw:4,hasadjust:[3,4],hash:0,hasn:6,have:[0,3,4,5,6],haven:0,hdr:[0,4,5],header:[],headlin:[0,6],heic:0,height:[0,4],help:[1,2,3,5,6],here:[0,6],hh:0,hhmm:0,hidden:[0,4,6],hierarch:[4,6],high:0,highest:4,highli:0,highlight:0,highlight_vis:4,hold:[4,5,6],holidai:[4,5],home:6,homebrew:[0,2],hopefulli:6,hour:[0,5],how:[0,5,6],howev:6,hr:0,html:0,http:[0,2,4,5,6],i:[0,4,5,6],icloud:[0,4,6],id:[0,2,4,5],identifi:[0,4,5,6],ignor:[0,4,6],ignore_cas:4,ignore_date_modifi:4,ignore_signatur:4,imag:[0,1,2,4,5],imagedescript:0,imagenam:5,img_1234:[0,4,6],img_1234_2021:6,img_1234_edit:6,immedi:[4,6],immers:4,implement:[0,4,5,6],import_info:4,importinfo:4,improv:[0,6],in_album:4,incloud:[0,4],includ:[0,2,3,4,5,6],incorrect:[4,5],increment:4,index:[1,4,5],indic:5,info:[1,2,4],inform:[0,2,3,4,5,6],initi:4,insensit:[0,5],insert:6,insid:6,inspect:[0,3],instal:[1,3,4,5,6],instanc:[3,4,6],instead:[0,4,5,6],instruct:[2,6],integ:[0,5],integr:6,intend:[0,6],intent:6,interact:[0,2,3,4],interest:[5,6],interesting_subject:4,interfac:[1,2,3,4,6],intermitt:0,intern:[5,6],internet:0,interpret:6,interrupt:[0,6],intrash:4,intrusive_object_pres:4,invalid:[3,4],invest:6,involv:6,io:[0,5],iphon:[5,6],iptc:[0,4,5],is_debug:4,is_refer:4,is_valid_filepath:3,iscloudasset:4,isdir:3,ishdr:5,ismin:4,ismiss:[3,4],ismovi:4,isn:6,iso:[0,4,5],isphoto:4,israw:4,isrefer:4,item:[0,4],iter:4,iterm2:0,its:[4,5,6],itself:[5,6],j:[0,4],john:3,join:[3,5,6],jpeg:[0,1,4,5],jpeg_ext:4,jpeg_qual:[0,4],jpg:[0,3,4,5,6],json:[0,2,4,6],julian:5,just:[0,3,5,6],keep:6,kei:[4,5,6],keybind:0,keyword:[1,2,3,4,5,6],keyword_templ:4,keywords_as_dict:[3,4],kind:6,know:[0,4],knowledg:5,known:6,l:0,label:[1,2,3,4,5,6],label_norm:5,labels_as_dict:4,labels_norm:4,labels_normalized_as_dict:4,landmark:5,languag:5,laps:[0,4],larg:[0,6],largest:4,last:[0,2,3,6],later:[0,5],latest:0,latitud:4,launch:0,lead:[0,4,6],learn:[4,5,6],left:6,legibl:6,len:[3,5],lens_model:[4,5],less:[5,6],level:[0,4],lf:5,librari:[0,1,2,3,4],library_path:[3,4],licens:[0,2],light:0,lightroom:[0,6],liglob:4,like:[0,3,4,5,6],likeinfo:4,likewis:3,limit:[5,6],line:[1,3,5,6],link:4,list:[1,2,3,4,5,6],liter:[4,6],littl:6,live:[0,4,5,6],live_photo:[4,5],lively_color:4,ll:[0,6],load:[0,1,3],local:[4,5,6],locality_nam:4,locat:[0,4,6],lock:3,log:[4,6],logic:6,longitud:4,look:[0,2,4,6],lookup:[4,6],loop:3,lose:[0,6],lot:0,low_light:4,lower:[0,4,5],lowest:4,m:[0,4,5],mac:[0,2,6],machin:[0,2,4,5,6],maco:[0,2,5],made:5,mai:[0,3,4,5,6],main:3,maintain:6,make:[0,3,5,6],makedir:3,manag:[0,2,4,6],mangl:4,mani:[0,6],manner:6,manual:[0,6],mark:[0,4,5,6],markup:4,master:[0,5],match:[0,4,5,6],matter:6,max:0,max_siz:4,maximum:[0,4],me:[],mean:[0,4,5],media:[4,5,6],media_typ:[4,5],memori:[0,4],mention:6,mere:6,merg:[0,4,6],merge_exif_keyword:4,merge_exif_person:4,messag:[0,2],metadata:[0,1,2,4,5],metering_mod:4,method:4,mib:0,microsoft:[0,6],might:[0,4,6],min:[0,5],min_siz:4,minimum:4,minor:0,minut:[0,5,6],miss:[0,1,3,4],missing_album:4,missing_burst:4,mkdtemp:4,mm:[0,5,6],mo:[0,4],mock:4,mode:[0,4,6],model:[4,5],modif:[0,4,5,6],modifi:[0,4,5,6],modification_d:4,modifyd:[0,4],modul:1,moment:[3,4],moment_pk:4,momentinfo:[3,4],mon:5,mono:0,monokai:0,monterei:[2,5],month:[4,5,6],more:[0,2,3,4,5,6],most:[0,6],motion:[0,4],mov:[0,4],move:6,movi:[0,4,6],mtime:4,much:6,multi:[0,4,5,6],multipl:[0,5,6],must:[0,4,5,6],my:[5,6],myalternatelibrari:6,mysteri:4,n:[4,5,6],na:6,naiv:4,name:[0,2,3,4,5,6],nativ:6,ndescript:6,necessari:3,need:[0,3,5,6],negat:5,neighborhood:4,neither:[0,2,4],nest:6,network:[0,6],never:6,nevertheless:6,new_them:0,newer:0,newli:4,newlin:[5,6],next:6,nist:0,no_com:4,no_descript:4,no_fil:4,no_lik:4,no_loc:4,no_plac:4,no_titl:4,nois:4,nolabel:6,non:[0,4,6],none:[3,4],nonetyp:[],nor:0,normal:[0,4,6],norsrc:4,not_burst:4,not_cloudasset:4,not_favorit:4,not_hdr:4,not_hidden:4,not_in_album:4,not_incloud:4,not_liv:4,not_miss:4,not_panorama:4,not_portrait:4,not_screenshot:4,not_selfi:4,not_shar:4,not_slow_mo:4,not_time_laps:4,note:[0,4,5,6],noth:6,nothdr:5,notic:4,now:[2,6],number:[0,5,6],nw:5,o:0,object:[0,3,4,5],objexplor:3,obvious:0,occasion:6,occur:[0,4,5],offer:6,offset:[0,4],offsettimeorigin:0,often:6,old:0,older:4,one:[0,4,5,6],onli:[0,1,2,4,5],op:4,open:[0,2,3,5,6],openbrac:5,openbracket:5,openparen:5,oper:[0,1,5,6],oppos:6,optim:6,option:[0,1,2,3,4,5],order:[0,2,4],org:[0,4,5,6],organ:[0,6],orient:4,origin:[0,2,3,4,5,6],original_filenam:[3,4],original_files:4,original_height:4,original_nam:[0,5,6],original_orient:4,original_width:4,os:[2,3,4],oserror:4,osxphoto:[],osxphotos_cmd_lin:5,osxphotos_export:[0,6],osxphotos_snapshot:0,osxphotos_vers:5,ot:6,other:[0,4,5,6],otherwis:[0,3,4,5,6],out:[0,2,6],output:[0,2,4,5,6],output_fil:0,outsid:4,over:[0,6],overal:[4,5],overrid:[0,5],overview:[0,1],overwrit:[0,4],own:[0,3,6],owner:4,p:[0,3],packag:[0,1,2],pad:5,page:1,pair:[0,4,5,6],panorama:[0,4,5],paramet:4,paren:5,parent:4,parenthes:5,part:[0,4,5,6],parti:0,partial:5,particip:0,particular:4,particularli:0,pass:[0,3,4,5,6],path:[0,2,3,4,5,6],path_deriv:4,path_edit:[3,4],path_edited_live_photo:4,path_live_photo:4,path_raw:4,path_sep:[4,5],pathlib:[3,4],pathvalid:3,pattern:6,per:0,perform:[0,5,6],period:[5,6],permit:0,person:[1,2,3,4,5,6],person_info:4,personinfo:4,personinimag:0,persons_as_dict:[3,4],philipp:6,philosophi:6,photo:[0,1,2,3,4,5],photo_ext:0,photo_filenam:0,photo_index:4,photo_or_video:5,photoexport:[3,4],photoinfo:[0,3,4,5],photokit:[0,4,6],photonam:[0,6],photoname_bearbeiten:0,photoname_edit:0,photoname_low_r:0,photoname_preview:0,photopr:6,photos_by_uuid:4,photos_librari:[0,2],photos_library_path:0,photosdb:[3,4],photoshop:0,photoslibrari:[0,2,3,6],phototempl:4,phrase:5,pick:6,pictur:[0,2,3,4,6],pid:4,pip:3,pipe:5,pipx:[2,3],pixel:4,pk:4,place:[1,2,4,5,6],place_nam:4,placeinfo:[3,4],plain:[0,6],platform:3,pleas:[],pleasant_camera_tilt:4,pleasant_composit:4,pleasant_light:4,pleasant_pattern:4,pleasant_perspect:4,pleasant_post_process:4,pleasant_reflect:4,pleasant_symmetri:4,png:0,popul:6,portrait:[0,4,5,6],posit:[0,2],possibl:[0,4,6],post:[0,1,3],postal:5,postal_cod:5,posttext:5,power:6,pre:[],preced:[0,5,6],prefer:6,prefix:4,present:[0,5],preserv:[0,6],pretext:5,preview:[0,4,5,6],preview_suffix:4,previou:[0,1],previous:[0,4,6],primari:[0,4,5],print:[0,2,3,4,6],privat:0,pro:6,probabl:6,process:[0,1,3,4,5],produc:[4,6],program:[0,6],programmat:3,progress:0,project:[0,4,5],project_info:4,projectinfo:4,promot:4,proper:6,properli:6,properti:[4,5],prototyp:0,provid:[0,2,4,5,6],provinc:5,pull:0,purpos:6,push:0,put:6,py:[0,5],pygment:0,pylint:[],pypi:[],python3:[],python:[0,1,2,5],python_fil:0,q:3,qualiti:[0,4,6],queri:[1,2,3,4,6],query_ev:4,query_funct:0,queryopt:[3,4],question:5,questionmark:5,quickli:[0,6],quicktim:[0,4],quik:6,quit:[3,6],quot:[5,6],r:[0,5],rais:4,ram:6,ramdb:[0,6],rang:[0,4,6],rate:0,rather:6,raw:[0,4,6],raw_origin:4,raw_photo:4,re:[0,6],read:[2,4,6],readabl:[4,6],readthedoc:[],reason:6,recent:[0,4],recommend:[2,6],record:4,refer:[0,4,5,6],referenc:0,regardless:4,regex:[0,4,6],region:[0,4],regular:[0,4,5,6],regularli:6,relat:4,releas:[],reliabl:0,rememb:6,remov:[0,4,6],renam:[0,4,5,6],render:[0,4,5,6],render_opt:4,render_templ:4,rendered_str:4,renderopt:4,repeat:[0,6],repeatedli:6,repl:[1,2],replac:[0,4,5,6],replace_keyword:4,repo:[],report:[0,1],repres:[0,4,5],represent:4,reprocess:5,requir:[0,2,4,5,6],resolut:0,resolv:5,respect:4,restart:6,restaur:5,result:[0,3,4,5,6],resum:6,retri:[0,6],retriev:6,reus:0,revers:[0,4,5,6],rhet:[],rhettbul:[0,5],rich:[0,4,6],rise:6,rmdir:4,rn:5,roll:6,rule:4,run:[1,2,4,5],run_command:4,s:[0,2,4,5,6],safe:5,same:[0,2,3,4,5,6],sample_r:4,sanitize_filepath:3,save:[0,1],score:[3,4,5],scoreinfo:[3,4],screenshot:[0,4,5,6],script:[0,3],search:[0,1,2,4,6],search_info:4,search_info_norm:4,searchinfo:[3,4,5],season:[4,5],sec:[0,5],second:[0,3,4,5],section:6,secur:4,see:[0,4,5,6],seem:6,seen:6,select:[0,2,3,4,6],self:4,selfi:[0,4,5],semicolon:5,sensit:5,separ:[4,5,6],seq:5,sequenc:5,sequenti:5,session:[3,4],set:[0,4,5,6],set_config:4,set_debug:4,set_photoinfo_for_uuid:4,setup:[],setuptool:[],setvalu:4,sever:6,sh:[0,2],share:[0,3,4,5,6],sharply_focused_subject:4,shell:[0,2,3,5,6],shell_quot:[0,5,6],shot:0,should:[0,2,5,6],show:[0,2,3,4,6],shutter_spe:4,si:0,sidecar:[0,1,4],sidecar_drop_ext:4,sidecar_exiftool:4,sidecar_exiftool_skip:[0,4],sidecar_exiftool_written:[0,4],sidecar_ext:[0,6],sidecar_json:4,sidecar_json_skip:[0,4],sidecar_json_written:[0,4],sidecar_xmp:4,sidecar_xmp_skip:[0,4],sidecar_xmp_written:[0,4],sierra:2,signatur:[0,4,6],signific:6,silent:4,similar:5,simpl:3,simplest:5,simpli:6,sinc:[0,6],singl:[0,4,5,6],size:[0,4,6],skip:[0,3,4,6],skipped_album:4,slideshow:[4,5],slow:[0,4,5,6],slow_mo:[4,5],smart:[0,6],smith:3,snap:[1,2],snapshot:[0,2],so:[0,4,5,6],softwar:0,some:[0,4,5,6],someth:[4,6],somewhat:[0,6],sort:4,sort_ord:4,sourc:4,space:[0,5,6],special:[4,5,6],specif:[0,2,4,6],specifi:[0,1,2,3,4,5],spend:6,sport:5,spot:6,spotlight:[0,2,3,6],sql:4,sqldiff:0,sqlite3:4,sqlite:3,src:4,src_file:4,ss:0,st:5,stabl:[],stack:4,stamp:0,standard:6,start:[0,3,4,5,6],start_dat:4,startswith:5,stat:4,state:[0,4,5,6],state_abbrevi:4,state_provinc:5,statement:[4,5,6],statu:[0,4],stdout:0,still:[4,6],stop:4,storag:6,storat:4,store:[0,4,6],str:4,street:[4,5],strftime:5,string:[0,4,5,6],strip:[0,4,5,6],struct:4,structur:1,style:[0,6],sub:[4,5],subfield:[4,5],subfold:[0,4,5],subject:0,subsequ:0,subset:6,substitut:[1,4],subtitl:4,subtop:0,succeed:4,success:4,suffix:[0,4,6],suit:6,suitabl:0,summer:[5,6],suppli:4,support:[1,5],sur:[],sure:[0,3,6],sy:3,symbol:5,synch:[0,4,6],syntax:[0,5,6],system:[0,1,4,6],t:[0,4,6],tab:5,tabl:4,tag:[0,2,4,5,6],tag_group:4,tagnam:[0,5,6],tagslist:0,take:[5,6],taken:[0,4,6],tastefully_blur:4,tell:6,tempfil:4,templat:[0,1,3,4,6],template_field:5,template_filt:5,template_funct:5,template_str:4,temporari:[0,3,4],temporarydirectori:4,term:[4,6],termin:[0,2,6],test:[0,2,3,4,6],text:[0,4,5,6],text_detection_confidence_threshold:4,than:[0,3,4,5,6],thank:6,thei:[0,4,6],them:[0,4,6],theme:[1,2],thi:[0,2,3,4,5,6],thing:[5,6],think:6,third:0,those:[0,3,6],thousand:6,threshold:[4,5],through:[2,6],throughout:6,thu:[0,6],thusli:0,time:[0,2,4,5,6],time_laps:[4,5],timecr:0,timeout:4,timestamp:0,timewarp:[1,2],timezon:[0,2,4],titl:[0,3,4,5,6],titlecas:5,titlen:6,tmp:[0,4],tmpdir:[0,4],to_dat:[0,4],to_tim:[0,4],to_touch:4,todai:[0,5],togeth:6,toml:[0,6],took:3,tool:6,top:[0,4],topic:0,touch:[0,4,6],touch_fil:4,town:4,track:6,track_format:4,trail:[0,6],trash:[3,4],travel:[5,6],treat:[0,4,5,6],tremend:6,trigger:[0,4],tupl:4,tutori:[1,2],two:[0,2,6],txt:[0,6],type:[0,2,3,4,5,6],typeerror:4,tzoffset:4,u:[0,5],underli:5,underscor:6,understand:6,unedit:[3,6],unfil:3,uniform:[0,4],uninstal:[1,2,3],union:4,uniqu:[0,2,5],unit:[0,5],univers:5,unless:[0,4],unlik:[0,5,6],unlink:4,unmatch:4,unmodifi:5,unquot:5,unreli:6,until:4,up:[0,4,6],updat:[0,1,4,5],upgrad:0,upon:[0,4,6],upper:5,us:[0,1,2,4,5,6],usag:[0,1,3,5],use_albums_as_keyword:4,use_persons_as_keyword:4,use_photokit:4,use_photos_export:4,useabl:0,user:[1,3,4,5],userdata:0,usr:6,usual:[0,6],utc:[0,4],uti:[0,4],uti_edit:4,uti_origin:4,uti_raw:4,util:[0,2,4],utim:4,uuid:[1,2,3,4,5],v:[0,2],vacat:[4,5,6],valid:[0,3,4,5,6],valu:[0,4,5,6],value_if_fals:5,value_if_tru:5,valueerror:4,variabl:[0,3],varieti:6,variou:4,ve:[0,4],venu:[4,5],venue_typ:[4,5],verbos:[0,4,6],veri:[2,6],verifi:[3,6],versa:2,version:[1,2,3,4,5,6],vertic:5,vi:0,via:[0,4,6],vice:2,video:[0,4,5,6],view:0,viewer:3,virtual:[0,3],visbl:4,visibl:4,volum:[0,6],w:0,wa:[0,4,5,6],wai:[2,6],want:[0,3,5,6],warn:[0,4],washington:5,water:4,websit:6,wed:4,week:[0,5],well:[0,2,3,4,5,6],well_chosen_subject:4,well_framed_subject:4,well_timed_shot:4,went:0,were:[0,4,6],what:[0,1,4,5,6],when:[0,4,5,6],where:[0,5,6],whether:0,which:[0,3,4,5,6],white:5,white_bal:4,whitespac:[0,4,5,6],whose:0,wide:6,width:[0,4],window:0,wipe:6,within:0,without:[0,4,6],won:6,word:5,work:[0,2,4,5,6],workflow:6,worst:6,worth:6,would:[0,4,5,6],write:[0,4,6],written:[0,4,5,6],wrong:0,x:2,xattr:[0,6],xattr_skip:4,xattr_written:4,xmp:[0,4,6],y:[0,5],ye:0,year:[0,4,5,6],yet:[4,5,6],you:[0,2,3,4,5,6],your:[0,1,2,3,5],yy:5,yyyi:0,z:0,zero:5},titles:["OSXPhotos Command Line Interface (CLI)","Welcome to OSXPhotos\u2019s documentation!","OSXPhotos","OSXPhotos Python Package Overview","OSXPhotos python API","OSXPhotos Template System","OSXPhotos Tutorial"],titleterms:{"200mb":[],"4":[],"5":[],"default":[],"export":[0,6],about:0,actual:6,add:[],album:0,all:6,an:6,api:4,ar:[],attribut:6,awail:[],base:[],big:[],certain:6,chang:[],cli:[0,3],code:3,color:6,command:[0,2,6],conclus:6,convert:6,countri:[],creat:6,creation:[],date:6,desktop:[],diff:0,digit:[],directori:6,disk:6,doc:0,document:1,dry:6,dump:0,edit:6,exampl:[3,6],exif:[],extern:6,file:6,filenam:6,find:[],finder:6,folder:[],from:6,full:[],git:[],group:[],help:0,higher:[],imag:6,indic:1,info:0,instal:[0,2],interfac:0,jpeg:6,json:[],keyword:0,kid:[],label:0,larger:[],librari:6,line:[0,2],list:0,load:6,media:[],metadata:6,miss:6,modul:[],month:[],name:[],necessari:[],nocountri:[],onli:6,oper:2,option:6,osxphoto:[0,1,2,3,4,5,6],ouput:[],output:[],overview:[3,6],packag:3,person:0,photo:6,pip:[],pipx:[],place:0,post:6,previou:6,print:[],process:6,python:[3,4],queri:0,repl:[0,3],report:6,repositori:[],result:[],run:[0,3,6],s:1,save:6,sidecar:6,snap:0,specifi:6,structur:6,substitut:5,support:2,system:[2,5],tabl:1,templat:5,than:[],them:[],theme:[0,6],timewarp:0,tutori:[0,6],type:[],uninstal:0,updat:6,us:3,usag:2,user:6,uuid:0,verbos:[],version:0,video:[],welcom:1,what:2,when:[],write:[],year:[],your:6}})
\ No newline at end of file
diff --git a/docsrc/source/overview.rst b/docsrc/source/overview.rst
index e4f6b9a3..20c2c14b 100644
--- a/docsrc/source/overview.rst
+++ b/docsrc/source/overview.rst
@@ -74,6 +74,7 @@ This package will install a command line utility called ``osxphotos`` that allow
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
diff --git a/osxphotos/cli/cli.py b/osxphotos/cli/cli.py
index af615895..4a17991f 100644
--- a/osxphotos/cli/cli.py
+++ b/osxphotos/cli/cli.py
@@ -25,6 +25,7 @@ from .query import query
from .repl import repl
from .snap_diff import diff, snap
from .theme import theme
+from .timewarp import timewarp
from .tutorial import tutorial
from .uuid import uuid
from .version import version
@@ -82,6 +83,7 @@ for command in [
run,
snap,
theme,
+ timewarp,
tutorial,
uninstall,
uuid,
diff --git a/osxphotos/cli/color_themes.py b/osxphotos/cli/color_themes.py
index a551f68d..a413dc17 100644
--- a/osxphotos/cli/color_themes.py
+++ b/osxphotos/cli/color_themes.py
@@ -22,23 +22,26 @@ __all__ = [
THEME_STYLES = [
+ "bar.back",
+ "bar.complete",
+ "bar.finished",
+ "bar.pulse",
+ "change",
"color",
"count",
"error",
"filename",
"filepath",
"highlight",
+ "no_change",
"num",
- "time",
- "uuid",
- "warning",
- "bar.back",
- "bar.complete",
- "bar.finished",
- "bar.pulse",
"progress.elapsed",
"progress.percentage",
"progress.remaining",
+ "time",
+ "tz",
+ "uuid",
+ "warning",
]
COLOR_THEMES = {
@@ -48,23 +51,26 @@ COLOR_THEMES = {
tags=["dark"],
styles={
# color pallette from https://github.com/dracula/dracula-theme
+ "bar.back": Style(color="rgb(68,71,90)"),
+ "bar.complete": Style(color="rgb(249,38,114)"),
+ "bar.finished": Style(color="rgb(80,250,123)"),
+ "bar.pulse": Style(color="rgb(98,114,164)"),
+ "change": Style(color="bright_red", bold=True),
"color": Style(color="rgb(248,248,242)"),
"count": Style(color="rgb(139,233,253)"),
"error": Style(color="rgb(255,85,85)", bold=True),
"filename": Style(color="rgb(189,147,249)", bold=True),
"filepath": Style(color="rgb(80,250,123)", bold=True),
"highlight": Style(color="#000000", bgcolor="#d73a49", bold=True),
+ "no_change": Style(color="bright_green", bold=True),
"num": Style(color="rgb(139,233,253)", bold=True),
- "time": Style(color="rgb(139,233,253)", bold=True),
- "uuid": Style(color="rgb(255,184,108)"),
- "warning": Style(color="rgb(241,250,140)", bold=True),
- "bar.back": Style(color="rgb(68,71,90)"),
- "bar.complete": Style(color="rgb(249,38,114)"),
- "bar.finished": Style(color="rgb(80,250,123)"),
- "bar.pulse": Style(color="rgb(98,114,164)"),
"progress.elapsed": Style(color="rgb(139,233,253)"),
"progress.percentage": Style(color="rgb(255,121,198)"),
"progress.remaining": Style(color="rgb(139,233,253)"),
+ "time": Style(color="rgb(139,233,253)", bold=True),
+ "tz": Style(color="bright_cyan", bold=True),
+ "uuid": Style(color="rgb(255,184,108)"),
+ "warning": Style(color="rgb(241,250,140)", bold=True),
# "headers": Style(color="rgb(165,194,97)"),
# "options": Style(color="rgb(255,198,109)"),
# "metavar": Style(color="rgb(12,125,157)"),
@@ -74,23 +80,26 @@ COLOR_THEMES = {
name="light",
description="Light mode theme",
styles={
+ "bar.back": Style(color="grey23"),
+ "bar.complete": Style(color="rgb(249,38,114)"),
+ "bar.finished": Style(color="rgb(114,156,31)"),
+ "bar.pulse": Style(color="rgb(249,38,114)"),
+ "change": "bold dark_red",
"color": Style(color="#000000"),
"count": Style(color="#005cc5", bold=True),
"error": Style(color="#b31d28", bold=True, underline=True, italic=True),
"filename": Style(color="#6f42c1", bold=True),
"filepath": Style(color="#22863a", bold=True),
"highlight": Style(color="#ffffff", bgcolor="#d73a49", bold=True),
+ "no_change": "bold dark_green",
"num": Style(color="#005cc5", bold=True),
- "time": Style(color="#032f62", bold=True),
- "uuid": Style(color="#d73a49", bold=True),
- "warning": Style(color="#e36209", bold=True, underline=True, italic=True),
- "bar.back": Style(color="grey23"),
- "bar.complete": Style(color="rgb(249,38,114)"),
- "bar.finished": Style(color="rgb(114,156,31)"),
- "bar.pulse": Style(color="rgb(249,38,114)"),
"progress.elapsed": Style(color="#032f62", bold=True),
"progress.percentage": Style(color="#6f42c1", bold=True),
"progress.remaining": Style(color="#032f62", bold=True),
+ "time": Style(color="#032f62", bold=True),
+ "tz": "bold cyan",
+ "uuid": Style(color="#d73a49", bold=True),
+ "warning": Style(color="#e36209", bold=True, underline=True, italic=True),
# "headers": Style(color="rgb(254,212,66)"),
# "options": Style(color="rgb(227,98,9)"),
# "metavar": Style(color="rgb(111,66,193)"),
@@ -101,22 +110,25 @@ COLOR_THEMES = {
description="Monochromatic theme",
tags=["mono", "colorblind"],
styles={
+ "bar.back": "",
+ "bar.complete": "reverse",
+ "bar.finished": "bold",
+ "bar.pulse": "bold",
+ "change": "reverse",
"count": "bold",
"error": "reverse italic",
"filename": "bold",
"filepath": "bold underline",
"highlight": "reverse italic",
+ "no_change": "",
"num": "bold",
- "time": "bold",
- "uuid": "bold",
- "warning": "bold italic",
- "bar.back": "",
- "bar.complete": "reverse",
- "bar.finished": "bold",
- "bar.pulse": "bold",
"progress.elapsed": "",
"progress.percentage": "bold",
"progress.remaining": "bold",
+ "time": "bold",
+ "tz": "",
+ "uuid": "bold",
+ "warning": "bold italic",
# "headers": "bold",
# "options": "bold",
# "metavar": "bold",
@@ -127,23 +139,26 @@ COLOR_THEMES = {
description="Plain theme with no colors",
tags=["colorblind"],
styles={
+ "bar.back": "",
+ "bar.complete": "",
+ "bar.finished": "",
+ "bar.pulse": "",
+ "change": "",
"color": "",
"count": "",
"error": "",
"filename": "",
"filepath": "",
"highlight": "",
+ "no_change": "",
"num": "",
- "time": "",
- "uuid": "",
- "warning": "",
- "bar.back": "",
- "bar.complete": "",
- "bar.finished": "",
- "bar.pulse": "",
"progress.elapsed": "",
"progress.percentage": "",
"progress.remaining": "",
+ "time": "",
+ "tz": "",
+ "uuid": "",
+ "warning": "",
# "headers": "",
# "options": "",
# "metavar": "",
@@ -162,7 +177,9 @@ def get_theme_dir() -> pathlib.Path:
def get_theme_manager() -> ThemeManager:
"""Return theme manager instance"""
- return ThemeManager(theme_dir=str(get_theme_dir()), themes=COLOR_THEMES.values())
+ return ThemeManager(
+ theme_dir=str(get_theme_dir()), themes=COLOR_THEMES.values(), update=True
+ )
def get_theme(
diff --git a/osxphotos/cli/common.py b/osxphotos/cli/common.py
index fa9e2e75..2f4bff17 100644
--- a/osxphotos/cli/common.py
+++ b/osxphotos/cli/common.py
@@ -569,3 +569,4 @@ def check_version():
"to suppress this message and prevent osxphotos from checking for latest version.",
err=True,
)
+
diff --git a/osxphotos/cli/param_types.py b/osxphotos/cli/param_types.py
index e9a424c9..b2d98303 100644
--- a/osxphotos/cli/param_types.py
+++ b/osxphotos/cli/param_types.py
@@ -5,19 +5,26 @@ import pathlib
import bitmath
import click
+import pytimeparse
from osxphotos.export_db_utils import export_db_get_version
from osxphotos.photoinfo import PhotoInfoNone
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
+from osxphotos.timeutils import time_string_to_datetime, utc_offset_string_to_seconds
+from osxphotos.timezones import Timezone
from osxphotos.utils import expand_and_validate_filepath, load_function
__all__ = [
"BitMathSize",
+ "DateOffset",
"DateTimeISO8601",
"ExportDBType",
"FunctionCall",
- "TimeISO8601",
"TemplateString",
+ "TimeISO8601",
+ "TimeOffset",
+ "TimeString",
+ "UTCOffset",
]
@@ -129,3 +136,75 @@ class TemplateString(click.ParamType):
return value
except ValueError as e:
self.fail(e)
+
+
+class TimeString(click.ParamType):
+ """A timestring in format HH:MM:SS, HH:MM:SS.fff, HH:MM"""
+
+ name = "TIMESTRING"
+
+ def convert(self, value, param, ctx):
+ try:
+ return time_string_to_datetime(value)
+ except ValueError:
+ self.fail(
+ f"Invalid time format: {value}. "
+ "Valid format for time: 'HH:MM:SS', 'HH:MM:SS.fff', 'HH:MM'"
+ )
+
+
+class DateOffset(click.ParamType):
+ """A date offset string in the format ±D days, ±W weeks, ±Y years, ±D where D is days"""
+
+ name = "DATEOFFSET"
+
+ def convert(self, value, param, ctx):
+ offset = pytimeparse.parse(value)
+ if offset is not None:
+ offset = offset / 86400
+ return datetime.timedelta(days=offset)
+
+ # could be in format "-1" (negative offset) or "+1" (positive offset)
+ try:
+ return datetime.timedelta(days=int(value))
+ except ValueError:
+ self.fail(
+ f"Invalid date offset format: {value}. "
+ "Valid format for date/time offset: '±D days', '±W weeks', '±D' where D is days "
+ )
+
+
+class TimeOffset(click.ParamType):
+ """A time offset string in the format [+-]HH:MM[:SS[.fff[fff]]] or +1 days, -2 hours, -18000, etc"""
+
+ name = "TIMEOFFSET"
+
+ def convert(self, value, param, ctx):
+ offset = pytimeparse.parse(value)
+ if offset is not None:
+ return datetime.timedelta(seconds=offset)
+
+ # could be in format "-18000" (negative offset) or "+18000" (positive offset)
+ try:
+ return datetime.timedelta(seconds=int(value))
+ except ValueError:
+ self.fail(
+ f"Invalid time offset format: {value}. "
+ "Valid format for date/time offset: '±HH:MM:SS', '±H hours' (or hr), '±M minutes' (or min), '±S seconds' (or sec), '±S' (where S is seconds)"
+ )
+
+
+class UTCOffset(click.ParamType):
+ """A UTC offset timezone in format ±[hh]:[mm], ±[h]:[mm], or ±[hh][mm]"""
+
+ name = "UTC_OFFSET"
+
+ def convert(self, value, param, ctx):
+ try:
+ offset_seconds = utc_offset_string_to_seconds(value)
+ return Timezone(offset_seconds)
+ except Exception:
+ self.fail(
+ f"Invalid timezone format: {value}. "
+ "Valid format for timezone offset: '±HH:MM', '±H:MM', or '±HHMM'"
+ )
diff --git a/osxphotos/cli/timewarp.py b/osxphotos/cli/timewarp.py
new file mode 100644
index 00000000..6037729a
--- /dev/null
+++ b/osxphotos/cli/timewarp.py
@@ -0,0 +1,542 @@
+""" Fix time / date / timezone for photos in Apple Photos """
+
+import datetime
+import os
+import sys
+from functools import partial
+from textwrap import dedent
+from typing import Callable
+
+import click
+from photoscript import Photo, PhotosLibrary
+from rich.console import Console
+
+from osxphotos._constants import APP_NAME
+from osxphotos.compare_exif import PhotoCompare
+from osxphotos.datetime_utils import datetime_naive_to_local, datetime_to_new_tz
+from osxphotos.exif_datetime_updater import ExifDateTimeUpdater
+from osxphotos.exiftool import get_exiftool_path
+from osxphotos.photosalbum import PhotosAlbumPhotoScript
+from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
+from osxphotos.timeutils import update_datetime
+from osxphotos.timezones import Timezone
+from osxphotos.utils import pluralize
+
+from .click_rich_echo import (
+ rich_click_echo,
+ rich_echo,
+ rich_echo_error,
+ set_rich_console,
+ set_rich_theme,
+ set_rich_timestamp,
+)
+from .color_themes import get_theme
+from .common import THEME_OPTION
+from .darkmode import is_dark_mode
+from .help import HELP_WIDTH, rich_text
+from .param_types import DateOffset, DateTimeISO8601, TimeOffset, TimeString, UTCOffset
+from .verbose import get_verbose_console, verbose_print
+
+# format for pretty printing date/times
+DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S%z"
+
+
+def update_photo_date_time(
+ photo: Photo,
+ date,
+ time,
+ date_delta,
+ time_delta,
+ verbose_print: Callable,
+):
+ """Update date, time in photo"""
+ photo_date = photo.date
+ new_photo_date = update_datetime(
+ photo_date, date=date, time=time, date_delta=date_delta, time_delta=time_delta
+ )
+ filename = photo.filename
+ uuid = photo.uuid
+ if new_photo_date != photo_date:
+ photo.date = new_photo_date
+ verbose_print(
+ f"Updated date/time for photo [filename]{filename}[/filename] "
+ f"([uuid]{uuid}[/uuid]) from: [time]{photo_date}[/time] to [time]{new_photo_date}[/time]"
+ )
+ else:
+ verbose_print(
+ f"Skipped date/time update for photo [filename]{filename}[/filename] "
+ f"([uuid]{uuid}[/uuid]): nothing to do"
+ )
+
+
+def update_photo_time_for_new_timezone(
+ library_path: str,
+ photo: Photo,
+ new_timezone: Timezone,
+ verbose_print: Callable,
+):
+ """Update time in photo to keep it the same time but in a new timezone
+
+ For example, photo time is 12:00+0100 and new timezone is +0200,
+ so adjust photo time by 1 hour so it will now be 12:00+0200 instead of
+ 13:00+0200 as it would be with no adjustment to the time"""
+ old_timezone = PhotoTimeZone(library_path=library_path).get_timezone(photo)[0]
+ # need to move time in opposite direction of timezone offset so that
+ # photo time is the same time but in the new timezone
+ delta = old_timezone - new_timezone.offset
+ photo_date = photo.date
+ new_photo_date = update_datetime(
+ dt=photo_date, time_delta=datetime.timedelta(seconds=delta)
+ )
+ filename = photo.filename
+ uuid = photo.uuid
+ if photo_date != new_photo_date:
+ photo.date = new_photo_date
+ verbose_print(
+ f"Adjusted date/time for photo [filename]{filename}[/filename] ([uuid]{uuid}[/uuid]) to match "
+ f"previous time [time]{photo_date}[time] but in new timezone [tz]{new_timezone}[/tz]."
+ )
+ else:
+ verbose_print(
+ f"Skipping date/time update for photo [filename]{filename}[/filename] ([uuid]{photo.uuid}[/uuid]), "
+ f"already matches new timezone [tz]{new_timezone}[/tz]"
+ )
+
+
+class TimeWarpCommand(click.Command):
+ """Custom click.Command that overrides get_help() to show additional help info for export"""
+
+ def get_help(self, ctx):
+ help_text = super().get_help(ctx)
+ formatter = click.HelpFormatter(width=HELP_WIDTH)
+ formatter.write("\n\n")
+ formatter.write(
+ rich_text(
+ dedent(
+ """
+# Timewarp Overview
+
+Timewarp operates on photos selected in Apple Photos. To use it, open Photos, select the photos for which you'd like to adjust the date/time/timezone, then run osxphotos timewarp from the command line:
+
+`osxphotos timewarp --date 2021-09-10 --time-delta "-1 hour" --timezone -0700 --verbose`
+
+This example sets the date for all selected photos to `2021-09-10`, subtracts 1 hour from the time of each photo, and sets the timezone of each photo to `GMT -07:00` (Pacific Daylight Time).
+
+osxphotos timewarp has been well tested on macOS Catalina (10.15). It should work on macOS Big Sur (11.0) and macOS Monterey (12.0) but I have not been able to test this. It will not work on macOS Mojave (10.14) or earlier as the Photos database format is different.
+
+**Caution**: This app directly modifies your Photos library database using undocumented features. It may corrupt, damage, or destroy your Photos library. Use at your own caution. I strongly recommend you make a backup of your Photos library before using this script (e.g. use Time Machine).
+
+## Examples
+
+**Add 1 day to the date of each photo**
+
+`osxphotos timewarp --date-delta 1`
+
+or
+
+`osxphotos timewarp --date-delta "+1 day"`
+
+**Set the date of each photo to 23 April 2020 and add 3 hours to the time**
+
+`osxphotos timewarp --date 2020-04-23 --time-delta "+3 hours"`
+
+or
+
+`osxphotos timewarp --date 2020-04-23 --time-delta "+03:00:00"`
+
+**Set the time of each photo to 14:30 and set the timezone to UTC +1:00 (Central European Time)**
+
+`osxphotos timewarp --time 14:30 --timezone +01:00`
+
+or
+
+`osxphotos timewarp --time 14:30 --timezone +0100`
+
+**Subtract 1 week from the date for each photo, add 3 hours to the time, set the timezone to UTC -07:00 (Pacific Daylight Time) and also use exiftool to update the EXIF metadata accordingly in the original file; use --verbose to print additional details**
+
+`osxphotos timewarp --date-delta "-1 week" --time-delta "+3 hours" --timezone -0700 --push-exif --verbose`
+
+For this to work, you'll need to install the third-party exiftool (https://exiftool.org/) utility. If you use homebrew (https://brew.sh/) you can do this with `brew install exiftool`.
+
+**Set the timezone to UTC +03:00 for each photo but keep the time the same (that is, don't adjust time for the new timezone)**
+
+`osxphotos timewarp --timezone 0300 --match-time`
+
+*Note on timezones and times*: In Photos, when you change the timezone, Photos assumes the time itself was correct for the previous timezone and adjusts the time accordingly to the new timezone. E.g. if the photo's time is `13:00` and the timezone is `GMT -07:00` and you adjust the timezone one hour east to `GMT -06:00`, Photos will change the time of the photo to `14:00`. osxphotos timewarp follows this behavior. Using `--match-time` allows you to adjust the timezone but keep the same time without adjustment. For example, if your camera clock was correct but lacked timezone information and you took photos in one timezone but imported them to photos in another, Photos will add the timezone of the computer at time of import. You can use osxphotos timewarp to adjust the timezone but keep the time using `--match-time`.
+
+**Compare the date/time/timezone of selected photos with the date/time/timezone in the photos' original EXIF metadata**
+
+`osxphotos timewarp --compare-exif`
+
+"""
+ ),
+ width=formatter.width,
+ markdown=True,
+ )
+ )
+ help_text += formatter.getvalue()
+ return help_text
+
+
+@click.command(cls=TimeWarpCommand, name="timewarp")
+@click.option(
+ "--date",
+ "-d",
+ metavar="DATE",
+ type=DateTimeISO8601(),
+ help="Set date for selected photos. Format is 'YYYY-MM-DD'.",
+)
+@click.option(
+ "--date-delta",
+ "-D",
+ metavar="DELTA",
+ type=DateOffset(),
+ help="Adjust date for selected photos by DELTA. "
+ "Format is one of: '±D days', '±W weeks', '±D' where D is days",
+)
+@click.option(
+ "--time",
+ "-t",
+ metavar="TIME",
+ type=TimeString(),
+ help="Set time for selected photos. Format is one of 'HH:MM:SS', 'HH:MM:SS.fff', 'HH:MM'.",
+)
+@click.option(
+ "--time-delta",
+ "-T",
+ metavar="DELTA",
+ type=TimeOffset(),
+ help="Adjust time for selected photos by DELTA time. "
+ "Format is one of '±HH:MM:SS', '±H hours' (or hr), '±M minutes' (or min), '±S seconds' (or sec), '±S' (where S is seconds)",
+)
+@click.option(
+ "--timezone",
+ "-z",
+ metavar="TIMEZONE",
+ type=UTCOffset(),
+ help="Set timezone for selected photos as offset from UTC. "
+ "Format is one of '±HH:MM', '±H:MM', or '±HHMM'. "
+ "The actual time of the photo is not adjusted which means, somewhat counterintuitively, "
+ "that the time in the new timezone will be different. "
+ "For example, if photo has time of 12:00 and timezone of GMT+01:00 and new timezone is specified as "
+ "'--timezone +02:00' (one hour ahead of current GMT+01:00 timezone), the photo's new time will be 13:00 GMT+02:00, "
+ "which is equivalent to the old time of 12:00+01:00. "
+ "This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. "
+ "See also --match-time. ",
+)
+@click.option(
+ "--inspect",
+ "-i",
+ is_flag=True,
+ help="Print out the date/time/timezone for each selected photo without changing any information.",
+)
+@click.option(
+ "--compare-exif",
+ "-c",
+ is_flag=True,
+ help="Compare the EXIF date/time/timezone for each selected photo to the same data in Photos. "
+ "Requires the third-party exiftool utility be installed (see https://exiftool.org/). "
+ "See also --add-to-album.",
+)
+@click.option(
+ "--push-exif",
+ "-p",
+ is_flag=True,
+ help="Push date/time and timezone for selected photos from Photos to the "
+ "EXIF metadata in the original file in the Photos library. "
+ "Requires the third-party exiftool utility be installed (see https://exiftool.org/). "
+ "Using this option modifies the *original* file of the image in your Photos library. "
+ "--push-exif will be executed after any other updates are performed on the photo. "
+ "See also --pull-exif.",
+)
+@click.option(
+ "--pull-exif",
+ "-P",
+ is_flag=True,
+ help="Pull date/time and timezone for selected photos from EXIF metadata in the original file "
+ "into Photos and update the associated data in Photos to match the EXIF data. "
+ "--pull-exif will be executed before any other updates are performed on the photo. "
+ "It is possible for images to have missing EXIF data, for example the date/time could be set but there might be "
+ "no timezone set in the EXIF metadata. "
+ "Missing data will be handled thusly: if date/time/timezone are all present in the EXIF data, "
+ "the photo's date/time/timezone will be updated. If timezone is missing but date/time is present, "
+ "only the photo's date/time will be updated. If date/time is missing but the timezone is present, only the "
+ "photo's timezone will be updated unless --use-file-time is set in which case, "
+ "the photo's file modification date/time will be used in place of EXIF date/time. "
+ "If the date is present but the time is missing, the time will be set to 00:00:00. "
+ "Requires the third-party exiftool utility be installed (see https://exiftool.org/). "
+ "See also --push-exif.",
+)
+# constraint=RequireAtLeast(1),
+# @constraint(mutually_exclusive, ["date", "date_delta"])
+# @constraint(mutually_exclusive, ["time", "time_delta"])
+@click.option(
+ "--match-time",
+ "-m",
+ is_flag=True,
+ help="When used with --timezone, adjusts the photo time so that the timestamp in the new timezone matches "
+ "the timestamp in the old timezone. "
+ "For example, if photo has time of 12:00 and timezone of GMT+01:00 and new timezone is specified as "
+ "'--timezone +02:00' (one hour ahead of current GMT+01:00 timezone), the photo's new time will be 12:00 GMT+02:00. "
+ "That is, the timezone will have changed but the timestamp of the photo will match the previous timestamp. "
+ "Use --match-time when the camera's time was correct for the time the photo was taken but the "
+ "timezone was missing or wrong and you want to adjust the timezone while preserving the photo's time. "
+ "See also --timezone.",
+)
+@click.option(
+ "--use-file-time",
+ "-f",
+ is_flag=True,
+ help="When used with --pull-exif, the file modification date/time will be used if date/time "
+ "is missing from the EXIF data. ",
+)
+@click.option(
+ "--add-to-album",
+ "-a",
+ metavar="ALBUM",
+ help="When used with --compare-exif, adds any photos with date/time/timezone differences "
+ "between Photos/EXIF to album ALBUM. If ALBUM does not exist, it will be created.",
+)
+@click.option("--verbose", "-V", "verbose", is_flag=True, help="Show verbose output.")
+@click.option(
+ "--library",
+ "-L",
+ metavar="PHOTOS_LIBRARY_PATH",
+ type=click.Path(),
+ help=r"Path to Photos library (e.g. '~/Pictures/Photos\ Library.photoslibrary'). "
+ f"This is not likely needed as {APP_NAME} will usually be able to locate the path to the open Photos library. "
+ "Use --library only if you get an error that the Photos library cannot be located.",
+)
+@click.option(
+ "--exiftool-path",
+ "-e",
+ type=click.Path(exists=True),
+ help="Optional path to exiftool executable (will look in $PATH if not specified) for those options which require exiftool.",
+)
+@click.option(
+ "--output-file",
+ "-o",
+ type=click.File(mode="w", lazy=False),
+ help="Output file. If not specified, output is written to stdout.",
+)
+@click.option(
+ "--terminal-width",
+ "-w",
+ type=int,
+ help="Terminal width in characters.",
+ hidden=True,
+)
+# @constraint(mutually_exclusive, ["plain", "mono", "dark", "light"])
+# @constraint(If("match_time", then=requires_one), ["timezone"])
+# @constraint(If("add_to_album", then=requires_one), ["compare_exif"])
+@click.option("--timestamp", is_flag=True, help="Add time stamp to verbose output")
+@THEME_OPTION
+@click.option(
+ "--plain",
+ is_flag=True,
+ help="Plain text mode. Do not use rich output.",
+)
+def timewarp(
+ date,
+ date_delta,
+ time,
+ time_delta,
+ timezone,
+ inspect,
+ compare_exif,
+ push_exif,
+ pull_exif,
+ match_time,
+ use_file_time,
+ add_to_album,
+ exiftool_path,
+ verbose,
+ library,
+ theme,
+ plain,
+ output_file,
+ terminal_width,
+ timestamp,
+):
+ """Adjust date/time/timezone of photos in Apple Photos.
+
+ Changes will be applied to all photos currently selected in Photos.
+ timewarp cannot operate on photos selected in a Smart Album;
+ select photos in a regular album or in the 'All Photos' view.
+ See Timewarp Overview below for additional information.
+ """
+
+ color_theme = get_theme(theme)
+ verbose_ = verbose_print(
+ verbose,
+ timestamp,
+ rich=True,
+ theme=color_theme,
+ highlight=False,
+ file=output_file,
+ )
+ # set console for rich_echo to be same as for verbose_
+ terminal_width = terminal_width or (1000 if output_file else None)
+ if output_file:
+ set_rich_console(Console(file=output_file, width=terminal_width))
+ elif terminal_width:
+ set_rich_console(
+ Console(file=sys.stdout, force_terminal=True, width=terminal_width)
+ )
+ else:
+ set_rich_console(get_verbose_console())
+ set_rich_theme(color_theme)
+
+ if any([compare_exif, push_exif, pull_exif]):
+ exiftool_path = exiftool_path or get_exiftool_path()
+ verbose_(f"exiftool path: [filename]{exiftool_path}[/filename]")
+
+ try:
+ photos = PhotosLibrary().selection
+ if not photos:
+ rich_echo_error("[warning]No photos selected[/]")
+ sys.exit(0)
+ except Exception as e:
+ # AppleScript error -1728 occurs if user attempts to get selected photos in a Smart Album
+ if "(-1728)" in str(e):
+ rich_echo_error(
+ "[error]Could not get selected photos. Ensure photos is open and photos are selected. "
+ "If you have selected photos and you see this message, it may be because the selected photos are in a Photos Smart Album. "
+ f"{APP_NAME} cannot access photos in a Smart Album. Select the photos in a regular album or in 'All Photos' view. "
+ "Another option is to create a new album using 'File | New Album With Selection' then select the photos in the new album.[/]",
+ )
+ else:
+ rich_echo_error(
+ f"[error]Could not get selected photos. Ensure Photos is open and photos to process are selected. {e}[/]",
+ )
+ sys.exit(1)
+
+ update_photo_date_time_ = partial(
+ update_photo_date_time,
+ date=date,
+ time=time,
+ date_delta=date_delta,
+ time_delta=time_delta,
+ verbose_print=verbose_,
+ )
+
+ update_photo_time_for_new_timezone_ = partial(
+ update_photo_time_for_new_timezone,
+ library_path=library,
+ verbose_print=verbose_,
+ )
+
+ if inspect:
+ tzinfo = PhotoTimeZone(library_path=library)
+ if photos:
+ rich_echo(
+ "[filename]filename[/filename], [uuid]uuid[/uuid], [time]photo time (local)[/time], [time]photo time[/time], [tz]timezone offset[/tz], [tz]timezone name[/tz]"
+ )
+ for photo in photos:
+ tz_seconds, tz_str, tz_name = tzinfo.get_timezone(photo)
+ photo_date_local = datetime_naive_to_local(photo.date)
+ photo_date_tz = datetime_to_new_tz(photo_date_local, tz_seconds)
+ rich_echo(
+ f"[filename]{photo.filename}[/filename], [uuid]{photo.uuid}[/uuid], [time]{photo_date_local.strftime(DATETIME_FORMAT)}[/time], [time]{photo_date_tz.strftime(DATETIME_FORMAT)}[/time], [tz]{tz_str}[/tz], [tz]{tz_name}[/tz]"
+ )
+ sys.exit(0)
+
+ if compare_exif:
+ album = PhotosAlbumPhotoScript(add_to_album) if add_to_album else None
+ different_photos = 0
+ if photos:
+ photocomp = PhotoCompare(
+ library_path=library,
+ verbose=verbose_,
+ exiftool_path=exiftool_path,
+ )
+ if not album:
+ rich_echo(
+ "filename, uuid, photo time (Photos), photo time (EXIF), timezone offset (Photos), timezone offset (EXIF)"
+ )
+ for photo in photos:
+ diff_results = (
+ photocomp.compare_exif_with_markup(photo)
+ if not plain
+ else photocomp.compare_exif_no_markup(photo)
+ )
+ if not plain:
+ filename = (
+ f"[change]{photo.filename}[/change]"
+ if diff_results.diff
+ else f"[no_change]{photo.filename}[/no_change]"
+ )
+ else:
+ filename = photo.filename
+ uuid = f"[uuid]{photo.uuid}[/uuid]"
+ if album:
+ if diff_results.diff:
+ different_photos += 1
+ verbose_(
+ f"Photo {filename} ({uuid}) has different date/time/timezone, adding to album '{album.name}'"
+ )
+ album.add(photo)
+ else:
+ verbose_(f"Photo {filename} ({uuid}) has same date/time/timezone")
+ else:
+ rich_echo(
+ f"{filename}, {uuid}, "
+ f"{diff_results.photos_date} {diff_results.photos_time}, {diff_results.exif_date} {diff_results.exif_time}, "
+ f"{diff_results.photos_tz}, {diff_results.exif_tz}"
+ )
+ if album:
+ rich_echo(
+ f"Compared {len(photos)} photos, found {different_photos} "
+ f"that {pluralize(different_photos, 'is', 'are')} different and "
+ f"added {pluralize(different_photos, 'it', 'them')} to album '{album.name}'."
+ )
+ sys.exit(0)
+
+ if timezone:
+ tz_updater = PhotoTimeZoneUpdater(
+ timezone, verbose=verbose_, library_path=library
+ )
+
+ if any([push_exif, pull_exif]):
+ exif_updater = ExifDateTimeUpdater(
+ library_path=library,
+ verbose=verbose_,
+ exiftool_path=exiftool_path,
+ plain=plain,
+ )
+
+ rich_echo(f"Processing {len(photos)} {pluralize(len(photos), 'photo', 'photos')}")
+ # send progress bar output to /dev/null if verbose to hide the progress bar
+ fp = open(os.devnull, "w") if verbose else None
+ with click.progressbar(photos, file=fp) as bar:
+ for p in bar:
+ if pull_exif:
+ exif_updater.update_photos_from_exif(
+ p, use_file_modify_date=use_file_time
+ )
+ if any([date, time, date_delta, time_delta]):
+ update_photo_date_time_(p)
+ if match_time:
+ # need to adjust time before the timezone is updated
+ # or the old timezone will be overwritten in the database
+ update_photo_time_for_new_timezone_(photo=p, new_timezone=timezone)
+ if timezone:
+ tz_updater.update_photo(p)
+ if push_exif:
+ # this should be the last step in the if chain to ensure all Photos data is updated
+ # before exiftool is run
+ exif_warn, exif_error = exif_updater.update_exif_from_photos(p)
+ if exif_warn:
+ rich_echo_error(
+ f"[warning]Warning running exiftool: {exif_warn}[/]"
+ )
+ if exif_error:
+ rich_echo_error(f"[error]Error running exiftool: {exif_error}[/]")
+
+ if fp is not None:
+ fp.close()
+
+ rich_echo("Done.")
+
+ # if output_file:
+ # output_file.close()
diff --git a/osxphotos/cli/verbose.py b/osxphotos/cli/verbose.py
index cce491b8..ccacbd12 100644
--- a/osxphotos/cli/verbose.py
+++ b/osxphotos/cli/verbose.py
@@ -61,6 +61,7 @@ def verbose_print(
rich: bool = False,
highlight: bool = False,
theme: t.Optional[Theme] = None,
+ file: t.Optional[t.IO] = None,
**kwargs: t.Any,
) -> t.Callable:
"""Create verbose function to print output
@@ -71,6 +72,7 @@ def verbose_print(
rich: use rich.print instead of click.echo
highlight: if True, use automatic rich.print highlighting
theme: optional rich.theme.Theme object to use for formatting
+ file: optional file handle to write to instead of stdout
kwargs: any extra arguments to pass to click.echo or rich.print depending on whether rich==True
Returns:
@@ -80,7 +82,10 @@ def verbose_print(
return noop
global _console
- _console.console = Console(theme=theme, width=10_000)
+ if file:
+ _console.console = Console(theme=theme, file=file)
+ else:
+ _console.console = Console(theme=theme, width=10_000)
# closure to capture timestamp
def verbose_(*args):
diff --git a/osxphotos/compare_exif.py b/osxphotos/compare_exif.py
new file mode 100644
index 00000000..f7516ba1
--- /dev/null
+++ b/osxphotos/compare_exif.py
@@ -0,0 +1,167 @@
+""" PhotoCompare class to compare date/time/timezone in Photos to the exif data """
+
+from collections import namedtuple
+from typing import Callable, List, Optional, Tuple
+
+from osxphotos import PhotosDB
+from osxphotos.exiftool import ExifTool
+from photoscript import Photo
+
+from .datetime_utils import datetime_naive_to_local, datetime_to_new_tz
+from .exif_datetime_updater import get_exif_date_time_offset
+from .phototz import PhotoTimeZone
+from .utils import noop
+
+ExifDiff = namedtuple(
+ "ExifDiff",
+ [
+ "diff",
+ "photos_date",
+ "photos_time",
+ "photos_tz",
+ "exif_date",
+ "exif_time",
+ "exif_tz",
+ ],
+)
+
+
+def change(msg: str) -> str:
+ """Add change tag to string"""
+ return f"[change]{msg}[/change]"
+
+
+def no_change(msg: str) -> str:
+ """Add no change tag to string"""
+ return f"[no_change]{msg}[/no_change]"
+
+
+class PhotoCompare:
+ """Class to compare date/time/timezone in Photos to the exif data"""
+
+ def __init__(
+ self,
+ library_path: Optional[str] = None,
+ verbose: Optional[Callable] = None,
+ exiftool_path: Optional[str] = None,
+ ):
+ self.library_path = library_path
+ self.db = PhotosDB(self.library_path)
+ self.verbose = verbose or noop
+ self.exiftool_path = exiftool_path
+ self.phototz = PhotoTimeZone(self.library_path)
+
+ def compare_exif(self, photo: Photo) -> List[str]:
+ """Compare date/time/timezone in Photos to the exif data
+
+ Args:
+ photo (Photo): Photo object to compare
+
+ Returns:
+ List of strings:
+ """
+ photos_offset_seconds, photos_tz_str, _ = self.phototz.get_timezone(photo)
+ photos_date = datetime_naive_to_local(photo.date)
+ photos_date = datetime_to_new_tz(photos_date, photos_offset_seconds)
+ photos_date_str = photos_date.strftime("%Y-%m-%d %H:%M:%S")
+
+ photo_ = self.db.get_photo(photo.uuid)
+ if photo_path := photo_.path:
+ exif = ExifTool(filepath=photo_path, exiftool=self.exiftool_path)
+ exif_dict = exif.asdict()
+ exif_dt_offset = get_exif_date_time_offset(exif_dict)
+ exif_offset = exif_dt_offset.offset_str
+ exif_date = (
+ exif_dt_offset.datetime.strftime("%Y-%m-%d %H:%M:%S")
+ if exif_dt_offset.datetime
+ else ""
+ )
+ else:
+ exif_date = ""
+ exif_offset = ""
+
+ return [photos_date_str, photos_tz_str, exif_date, exif_offset]
+
+ def compare_exif_with_markup(self, photo: Photo) -> ExifDiff:
+ """Compare date/time/timezone in Photos to the exif data and return an ExifDiff named tuple;
+ adds rich markup to strings to show differences
+
+ Args:
+ photo (Photo): Photo object to compare
+ """
+ photos_date, photos_tz, exif_date, exif_tz = self.compare_exif(photo)
+ diff = False
+ photos_date, photos_time = photos_date.split(" ", 1)
+ try:
+ exif_date, exif_time = exif_date.split(" ", 1)
+ except ValueError:
+ exif_date = exif_date
+ exif_time = ""
+
+ if photos_date != exif_date:
+ photos_date = change(photos_date)
+ exif_date = change(exif_date)
+ diff = True
+ else:
+ photos_date = no_change(photos_date)
+ exif_date = no_change(exif_date)
+
+ if photos_time != exif_time:
+ photos_time = change(photos_time)
+ exif_time = change(exif_time)
+ diff = True
+ else:
+ photos_time = no_change(photos_time)
+ exif_time = no_change(exif_time)
+
+ if photos_tz != exif_tz:
+ photos_tz = change(photos_tz)
+ exif_tz = change(exif_tz)
+ diff = True
+ else:
+ photos_tz = no_change(photos_tz)
+ exif_tz = no_change(exif_tz)
+
+ return ExifDiff(
+ diff,
+ photos_date,
+ photos_time,
+ photos_tz,
+ exif_date,
+ exif_time,
+ exif_tz,
+ )
+
+ def compare_exif_no_markup(self, photo: Photo) -> ExifDiff:
+ """Compare date/time/timezone in Photos to the exif data and return an ExifDiff named tuple;
+
+ Args:
+ photo (Photo): Photo object to compare
+ """
+ photos_date, photos_tz, exif_date, exif_tz = self.compare_exif(photo)
+ diff = False
+ photos_date, photos_time = photos_date.split(" ", 1)
+ try:
+ exif_date, exif_time = exif_date.split(" ", 1)
+ except ValueError:
+ exif_date = exif_date
+ exif_time = ""
+
+ if photos_date != exif_date:
+ diff = True
+
+ if photos_time != exif_time:
+ diff = True
+
+ if photos_tz != exif_tz:
+ diff = True
+
+ return ExifDiff(
+ diff,
+ photos_date,
+ photos_time,
+ photos_tz,
+ exif_date,
+ exif_time,
+ exif_tz,
+ )
diff --git a/osxphotos/datetime_utils.py b/osxphotos/datetime_utils.py
index de7f2381..af9c5f6d 100644
--- a/osxphotos/datetime_utils.py
+++ b/osxphotos/datetime_utils.py
@@ -1,19 +1,28 @@
-""" datetime.datetime helper functions for converting to/from UTC """
+""" datetime.datetime helper functions for converting to/from UTC and other datetime manipulations"""
+
+# source: https://github.com/RhetTbull/datetime-utils
+
+__version__ = "2022.04.30"
import datetime
+# TODO: probably shouldn't use replace here, see this:
+# https://stackoverflow.com/questions/13994594/how-to-add-timezone-into-a-naive-datetime-instance-in-python/13994611#13994611
+
__all__ = [
- "get_local_tz",
"datetime_has_tz",
- "datetime_tz_to_utc",
- "datetime_remove_tz",
- "datetime_naive_to_utc",
"datetime_naive_to_local",
+ "datetime_naive_to_utc",
+ "datetime_remove_tz",
+ "datetime_to_new_tz",
+ "datetime_tz_to_utc",
"datetime_utc_to_local",
+ "get_local_tz",
+ "utc_offset_seconds",
]
-
-def get_local_tz(dt):
+# TODO: look at https://github.com/regebro/tzlocal for more robust implementation
+def get_local_tz(dt: datetime.datetime) -> datetime.tzinfo:
"""Return local timezone as datetime.timezone tzinfo for dt
Args:
@@ -31,7 +40,7 @@ def get_local_tz(dt):
raise ValueError("dt must be naive datetime.datetime object")
-def datetime_has_tz(dt):
+def datetime_has_tz(dt: datetime.datetime) -> bool:
"""Return True if datetime dt has tzinfo else False
Args:
@@ -50,7 +59,7 @@ def datetime_has_tz(dt):
return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
-def datetime_tz_to_utc(dt):
+def datetime_tz_to_utc(dt: datetime.datetime) -> datetime.datetime:
"""Convert datetime.datetime object with timezone to UTC timezone
Args:
@@ -70,10 +79,10 @@ def datetime_tz_to_utc(dt):
if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
return dt.replace(tzinfo=dt.tzinfo).astimezone(tz=datetime.timezone.utc)
else:
- raise ValueError(f"dt does not have timezone info")
+ raise ValueError("dt does not have timezone info")
-def datetime_remove_tz(dt):
+def datetime_remove_tz(dt: datetime.datetime) -> datetime.datetime:
"""Remove timezone from a datetime.datetime object
Args:
@@ -92,7 +101,7 @@ def datetime_remove_tz(dt):
return dt.replace(tzinfo=None)
-def datetime_naive_to_utc(dt):
+def datetime_naive_to_utc(dt: datetime.datetime) -> datetime.datetime:
"""Convert naive (timezone unaware) datetime.datetime
to aware timezone in UTC timezone
@@ -120,7 +129,7 @@ def datetime_naive_to_utc(dt):
return dt.replace(tzinfo=datetime.timezone.utc)
-def datetime_naive_to_local(dt):
+def datetime_naive_to_local(dt: datetime.datetime) -> datetime.datetime:
"""Convert naive (timezone unaware) datetime.datetime
to aware timezone in local timezone
@@ -142,13 +151,13 @@ def datetime_naive_to_local(dt):
# has timezone info
raise ValueError(
"dt must be naive/timezone unaware: "
- f"{dt} has tzinfo {dt.tzinfo} and offset {dt.tizinfo.utcoffset(dt)}"
+ f"{dt} has tzinfo {dt.tzinfo} and offset {dt.tzinfo.utcoffset(dt)}"
)
return dt.replace(tzinfo=get_local_tz(dt))
-def datetime_utc_to_local(dt):
+def datetime_utc_to_local(dt: datetime.datetime) -> datetime.datetime:
"""Convert datetime.datetime object in UTC timezone to local timezone
Args:
@@ -169,3 +178,33 @@ def datetime_utc_to_local(dt):
raise ValueError(f"{dt} must be in UTC timezone: timezone = {dt.tzinfo}")
return dt.replace(tzinfo=datetime.timezone.utc).astimezone(tz=None)
+
+
+def datetime_to_new_tz(dt: datetime.datetime, offset) -> datetime.datetime:
+ """Convert datetime.datetime object from current timezone to new timezone with offset of seconds from UTC"""
+ if not datetime_has_tz(dt):
+ raise ValueError("dt must be timezone aware")
+
+ time_delta = datetime.timedelta(seconds=offset)
+ tz = datetime.timezone(time_delta)
+ return dt.astimezone(tz=tz)
+
+
+def utc_offset_seconds(dt: datetime.datetime) -> int:
+ """Return offset in seconds from UTC for timezone aware datetime.datetime object
+
+ Args:
+ dt: datetime.datetime object
+
+ Returns:
+ offset in seconds from UTC
+
+ Raises:
+ ValueError if dt does not have timezone information
+ """
+
+ if dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None:
+ return dt.tzinfo.utcoffset(dt).total_seconds()
+ else:
+ raise ValueError("dt does not have timezone info")
+
diff --git a/osxphotos/docs/docs.zip b/osxphotos/docs/docs.zip
index 93d3e439..301241d5 100644
Binary files a/osxphotos/docs/docs.zip and b/osxphotos/docs/docs.zip differ
diff --git a/osxphotos/exif_datetime_updater.py b/osxphotos/exif_datetime_updater.py
new file mode 100644
index 00000000..9cb0dd82
--- /dev/null
+++ b/osxphotos/exif_datetime_updater.py
@@ -0,0 +1,336 @@
+"""Use exiftool to update exif data in photos """
+
+import datetime
+import re
+from collections import namedtuple
+from typing import Callable, Dict, Optional, Tuple
+
+from photoscript import Photo
+
+from .datetime_utils import (
+ datetime_has_tz,
+ datetime_naive_to_local,
+ datetime_remove_tz,
+ datetime_to_new_tz,
+ datetime_tz_to_utc,
+ datetime_utc_to_local,
+)
+from .exiftool import ExifTool
+from .photosdb import PhotosDB
+from .phototz import PhotoTimeZone, PhotoTimeZoneUpdater
+from .timezones import Timezone, format_offset_time
+from .utils import noop
+
+__all__ = ["ExifDateTime", "ExifDateTimeUpdater"]
+
+# date/time/timezone extracted from regex as a timezone aware datetime.datetime object
+# default_time is True if the time is not specified in the exif otherwise False (and if True, set to 00:00:00)
+# default_offset is True if timezone offset is not specified in the exif otherwise False (and if True, set to +00:00)
+# used_file_modify_date is True if the date/time is not specified in the exif and the FileModifyDate is used instead
+ExifDateTime = namedtuple(
+ "ExifDateTime",
+ [
+ "datetime",
+ "offset_seconds",
+ "offset_str",
+ "default_time",
+ "used_file_modify_date",
+ ],
+)
+
+
+def exif_offset_to_seconds(offset: str) -> int:
+ """Convert timezone offset from UTC in exiftool format (+/-hh:mm) to seconds"""
+ sign = 1 if offset[0] == "+" else -1
+ hours, minutes = offset[1:].split(":")
+ return sign * (int(hours) * 3600 + int(minutes) * 60)
+
+
+class ExifDateTimeUpdater:
+ """Update exif data in photos"""
+
+ def __init__(
+ self,
+ library_path: Optional[str] = None,
+ verbose: Optional[Callable] = None,
+ exiftool_path: Optional[str] = None,
+ plain=False,
+ ):
+ self.library_path = library_path
+ self.db = PhotosDB(self.library_path)
+ self.verbose = verbose or noop
+ self.exiftool_path = exiftool_path
+ self.tzinfo = PhotoTimeZone(library_path=self.library_path)
+ self.plain = plain
+
+ def filename_color(self, filename: str) -> str:
+ """Colorize filename for display in verbose output"""
+ return filename if self.plain else f"[filename]{filename}[/filename]"
+
+ def uuid_color(self, uuid: str) -> str:
+ """Colorize uuid for display in verbose output"""
+ return uuid if self.plain else f"[uuid]{uuid}[/uuid]"
+
+ def update_exif_from_photos(self, photo: Photo) -> Tuple[str, str]:
+ """Update EXIF data in photo to match the date/time/timezone in Photos library
+
+ Args:
+ photo: photoscript.Photo object to act on
+ """
+
+ # photo is the photoscript.Photo object passed in
+ # _photo is the osxphotos.PhotoInfo object for the same photo
+ # Need _photo to get the photo's path
+ _photo = self.db.get_photo(photo.uuid)
+ if not _photo:
+ raise ValueError(f"Photo {photo.uuid} not found")
+
+ if not _photo.path:
+ self.verbose(
+ "Skipping EXIF update for missing photo "
+ f"[filename]{_photo.original_filename}[/filename] ([uuid]{_photo.uuid}[/uuid])"
+ )
+ return "", ""
+
+ self.verbose(
+ "Updating EXIF data for "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid])"
+ )
+
+ photo_date = datetime_naive_to_local(photo.date)
+ timezone_offset = self.tzinfo.get_timezone(photo)[0]
+ photo_date = datetime_to_new_tz(photo_date, timezone_offset)
+
+ # exiftool expects format to "2015:01:18 12:00:00"
+ datetimeoriginal = photo_date.strftime("%Y:%m:%d %H:%M:%S")
+
+ # exiftool expects format of "-04:00"
+ offset = format_offset_time(timezone_offset)
+
+ # process date/time and timezone offset
+ # Photos exports the following fields and sets modify date to creation date
+ # [EXIF] Date/Time Original : 2020:10:30 00:00:00
+ # [EXIF] Create Date : 2020:10:30 00:00:00
+ # [IPTC] Digital Creation Date : 2020:10:30
+ # [IPTC] Date Created : 2020:10:30
+ #
+ # for videos:
+ # [QuickTime] CreateDate : 2020:12:11 06:10:10
+ # [Keys] CreationDate : 2020:12:10 22:10:10-08:00
+ exif = {}
+ if _photo.isphoto:
+ exif["EXIF:DateTimeOriginal"] = datetimeoriginal
+ exif["EXIF:CreateDate"] = datetimeoriginal
+ dateoriginal = photo_date.strftime("%Y:%m:%d")
+ exif["IPTC:DateCreated"] = dateoriginal
+ timeoriginal = photo_date.strftime(f"%H:%M:%S{offset}")
+ exif["IPTC:TimeCreated"] = timeoriginal
+
+ exif["EXIF:OffsetTimeOriginal"] = offset
+
+ elif _photo.ismovie:
+ # QuickTime spec specifies times in UTC
+ # QuickTime:CreateDate and ModifyDate are in UTC w/ no timezone
+ # QuickTime:CreationDate must include time offset or Photos shows invalid values
+ # reference: https://exiftool.org/TagNames/QuickTime.html#Keys
+ # https://exiftool.org/forum/index.php?topic=11927.msg64369#msg64369
+ creationdate = f"{datetimeoriginal}{offset}"
+ exif["QuickTime:CreationDate"] = creationdate
+
+ # need to convert to UTC then back to formatted string
+ tzdate = datetime.datetime.strptime(creationdate, "%Y:%m:%d %H:%M:%S%z")
+ utcdate = datetime_tz_to_utc(tzdate)
+ createdate = utcdate.strftime("%Y:%m:%d %H:%M:%S")
+ exif["QuickTime:CreateDate"] = createdate
+
+ self.verbose(
+ f"Writing EXIF data with exiftool to {self.filename_color(_photo.path)}"
+ )
+ with ExifTool(filepath=_photo.path, exiftool=self.exiftool_path) as exiftool:
+ for tag, val in exif.items():
+ if type(val) == list:
+ for v in val:
+ exiftool.setvalue(tag, v)
+ else:
+ exiftool.setvalue(tag, val)
+ return exiftool.warning, exiftool.error
+
+ def update_photos_from_exif(
+ self, photo: Photo, use_file_modify_date: bool = False
+ ) -> None:
+ """Update date/time/timezone in Photos library to match the data in EXIF
+
+ Args:
+ photo: photoscript.Photo object to act on
+ use_file_modify_date: if True, use the file modify date if there's no date/time in the exif data
+ """
+
+ # photo is the photoscript.Photo object passed in
+ # _photo is the osxphotos.PhotoInfo object for the same photo
+ # Need _photo to get the photo's path
+ _photo = self.db.get_photo(photo.uuid)
+ if not _photo:
+ raise ValueError(f"Photo {photo.uuid} not found")
+
+ if not _photo.path:
+ self.verbose(
+ "Skipping EXIF update for missing photo "
+ f"[filename]{_photo.original_filename}[/filename] ([uuid]{_photo.uuid}[/uuid])"
+ )
+ return None
+
+ self.verbose(
+ "Updating Photos from EXIF data for "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid])"
+ )
+
+ dtinfo = self.get_date_time_offset_from_exif(
+ _photo.path, use_file_modify_date=use_file_modify_date
+ )
+ if dtinfo.used_file_modify_date:
+ self.verbose(
+ "EXIF date/time missing, using file modify date/time for "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid])"
+ )
+ if not dtinfo.datetime and not dtinfo.offset_seconds:
+ self.verbose(
+ "Skipping update for missing EXIF data in photo "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid])"
+ )
+ return None
+
+ if dtinfo.offset_seconds:
+ # update timezone then update date/time
+ timezone = Timezone(dtinfo.offset_seconds)
+ tzupdater = PhotoTimeZoneUpdater(
+ library_path=self.library_path, timezone=timezone
+ )
+ tzupdater.update_photo(photo)
+ self.verbose(
+ "Updated timezone offset for photo "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid]): [tz]{timezone}[/tz]"
+ )
+
+ if dtinfo.datetime:
+ if datetime_has_tz(dtinfo.datetime):
+ # convert datetime to naive local time for setting in photos
+ local_datetime = datetime_remove_tz(
+ datetime_utc_to_local(datetime_tz_to_utc(dtinfo.datetime))
+ )
+ else:
+ local_datetime = dtinfo.datetime
+ # update date/time
+ photo.date = local_datetime
+ self.verbose(
+ "Updated date/time for photo "
+ f"[filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid]): [time]{local_datetime}[/time]"
+ )
+
+ return None
+
+ def get_date_time_offset_from_exif(
+ self, photo_path: str, use_file_modify_date: bool = False
+ ) -> ExifDateTime:
+ """Get date/time/timezone from EXIF data for a photo
+
+ Args:
+ photo_path: path to photo to get EXIF data from
+ use_file_modify_date: if True, use the file modify date if there's no date/time in the exif data
+
+ Returns:
+ ExifDateTime named tuple
+
+ """
+ exiftool = ExifTool(filepath=photo_path, exiftool=self.exiftool_path)
+ exif = exiftool.asdict()
+ return get_exif_date_time_offset(
+ exif, use_file_modify_date=use_file_modify_date
+ )
+
+
+def get_exif_date_time_offset(
+ exif: Dict, use_file_modify_date: bool = False
+) -> ExifDateTime:
+ """Get datetime/offset from an exif dict as returned by osxphotos.exiftool.ExifTool.asdict()
+
+ Args:
+ exif: dict of exif data
+ use_file_modify_date: if True, use the file modify date if there's no date/time in the exif data
+ """
+
+ # set to True if no time is found
+ default_time = False
+
+ # set to True if no date/time in EXIF and the FileModifyDate is used
+ used_file_modify_date = False
+
+ # search these fields in this order for date/time/timezone
+ time_fields = [
+ "EXIF:DateTimeOriginal",
+ "EXIF:CreateDate",
+ "QuickTime:CreationDate",
+ "QuickTime:CreateDate",
+ "IPTC:DateCreated",
+ "XMP-exif:DateTimeOriginal",
+ "XMP-xmp:CreateDate",
+ ]
+ if use_file_modify_date:
+ time_fields.append("File:FileModifyDate")
+
+ for dt_str in time_fields:
+ dt = exif.get(dt_str)
+ if dt and dt_str == "IPTC:DateCreated":
+ # also need time
+ time_ = exif.get("IPTC:TimeCreated")
+ if not time_:
+ time_ = "00:00:00"
+ default_time = True
+ dt = f"{dt} {time_}"
+
+ if dt:
+ used_file_modify_date = dt_str == "File:FileModifyDate"
+ break
+ else:
+ # no date/time found
+ dt = None
+
+ # try to get offset from EXIF:OffsetTimeOriginal
+ offset = exif.get("EXIF:OffsetTimeOriginal")
+ if dt and not offset:
+ # see if offset set in the dt string
+ matched = re.match(r"\d{4}:\d{2}:\d{2}\s\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2})", dt)
+ offset = matched.group(1) if matched else None
+
+ if dt:
+ # make sure we have time
+ matched = re.match(r"\d{4}:\d{2}:\d{2}\s(\d{2}:\d{2}:\d{2})", dt)
+ if not matched:
+ if matched := re.match(r"^(\d{4}:\d{2}:\d{2})", dt):
+ # set time to 00:00:00
+ dt = f"{matched.group(1)} 00:00:00"
+ default_time = True
+
+ offset_seconds = exif_offset_to_seconds(offset) if offset else None
+
+ if dt:
+ if offset:
+ # drop offset from dt string and add it back on in datetime %z format
+ dt = re.sub(r"[+-]\d{2}:\d{2}$", "", dt)
+ offset = offset.replace(":", "")
+ dt = f"{dt}{offset}"
+ dt_format = "%Y:%m:%d %H:%M:%S%z"
+ else:
+ dt_format = "%Y:%m:%d %H:%M:%S"
+
+ # convert to datetime
+ # some files can have bad date/time data, (e.g. #24, Date/Time Original = 0000:00:00 00:00:00)
+ try:
+ dt = datetime.datetime.strptime(dt, dt_format)
+ except ValueError:
+ dt = None
+
+ # format offset in form +/-hhmm
+ offset_str = offset.replace(":", "") if offset else ""
+ return ExifDateTime(
+ dt, offset_seconds, offset_str, default_time, used_file_modify_date
+ )
diff --git a/osxphotos/photosalbum.py b/osxphotos/photosalbum.py
index d2d983f5..9a266513 100644
--- a/osxphotos/photosalbum.py
+++ b/osxphotos/photosalbum.py
@@ -4,14 +4,17 @@ from typing import List, Optional
import photoscript
from more_itertools import chunked
+from photoscript import Photo, PhotosLibrary
from .photoinfo import PhotoInfo
-from .utils import noop
+from .utils import noop, pluralize
-__all__ = ["PhotosAlbum"]
+__all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"]
class PhotosAlbum:
+ """Add osxphotos.photoinfo.PhotoInfo objects to album"""
+
def __init__(self, name: str, verbose: Optional[callable] = None):
self.name = name
self.verbose = verbose or noop
@@ -39,9 +42,40 @@ class PhotosAlbum:
self.verbose(f"Error creating Photo object for photo {p.uuid}: {e}")
for photolist in chunked(photos, 10):
self.album.add(photolist)
- photo_len = len(photos)
- photo_word = "photos" if photo_len > 1 else "photo"
- self.verbose(f"Added {photo_len} {photo_word} to album {self.name}")
+ photo_len = len(photo_list)
+ self.verbose(
+ f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}"
+ )
+
+ def photos(self):
+ return self.album.photos()
+
+
+class PhotosAlbumPhotoScript:
+ """Add photoscript.Photo objects to album"""
+
+ def __init__(self, name: str, verbose: Optional[callable] = None):
+ self.name = name
+ self.verbose = verbose or noop
+ self.library = PhotosLibrary()
+
+ album = self.library.album(name)
+ if album is None:
+ self.verbose(f"Creating Photos album '{self.name}'")
+ album = self.library.create_album(name)
+ self.album = album
+
+ def add(self, photo: Photo):
+ self.album.add([photo])
+ self.verbose(f"Added {photo.filename} ({photo.uuid}) to album {self.name}")
+
+ def add_list(self, photo_list: List[Photo]):
+ for photolist in chunked(photo_list, 10):
+ self.album.add(photolist)
+ photo_len = len(photo_list)
+ self.verbose(
+ f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}"
+ )
def photos(self):
return self.album.photos()
diff --git a/osxphotos/phototz.py b/osxphotos/phototz.py
new file mode 100644
index 00000000..a71feddb
--- /dev/null
+++ b/osxphotos/phototz.py
@@ -0,0 +1,156 @@
+""" Update the timezone of a photo in Apple Photos' library """
+# WARNING: This is a hack. It might destroy your Photos library.
+# Ensure you have a backup before using!
+# You have been warned.
+
+import pathlib
+import sqlite3
+from typing import Callable, Optional, Tuple
+
+from photoscript import Photo
+from tenacity import retry, stop_after_attempt, wait_exponential
+
+from ._constants import _DB_TABLE_NAMES
+from .photosdb.photosdb_utils import get_photos_library_version
+from .timezones import Timezone
+from .utils import get_last_library_path, get_system_library_path, noop
+
+
+def tz_to_str(tz_seconds: int) -> str:
+ """convert timezone offset in seconds to string in form +00:00 (as offset from GMT)"""
+ sign = "+" if tz_seconds >= 0 else "-"
+ tz_seconds = abs(tz_seconds)
+ # get min and seconds first
+ mm, _ = divmod(tz_seconds, 60)
+ # Get hours
+ hh, mm = divmod(mm, 60)
+ return f"{sign}{hh:02}{mm:02}"
+
+
+class PhotoTimeZone:
+ """Get timezone info for photos"""
+
+ def __init__(
+ self,
+ library_path: Optional[str] = None,
+ ):
+ # get_last_library_path() returns the path to the last Photos library
+ # opened but sometimes (rarely) fails on some systems
+ try:
+ db_path = (
+ library_path or get_last_library_path() or get_system_library_path()
+ )
+ except Exception:
+ db_path = None
+ if not db_path:
+ raise FileNotFoundError("Could not find Photos database path")
+
+ photos_version = get_photos_library_version(db_path)
+ db_path = str(pathlib.Path(db_path) / "database/Photos.sqlite")
+ self.db_path = db_path
+ self.ASSET_TABLE = _DB_TABLE_NAMES[photos_version]["ASSET"]
+
+ def get_timezone(self, photo: Photo) -> Tuple[int, str, str]:
+ """Return (timezone_seconds, timezone_str, timezone_name) of photo"""
+ uuid = photo.uuid
+ sql = f""" SELECT
+ ZADDITIONALASSETATTRIBUTES.ZTIMEZONEOFFSET,
+ ZADDITIONALASSETATTRIBUTES.ZTIMEZONENAME
+ FROM ZADDITIONALASSETATTRIBUTES
+ JOIN {self.ASSET_TABLE}
+ ON ZADDITIONALASSETATTRIBUTES.ZASSET = {self.ASSET_TABLE}.Z_PK
+ WHERE {self.ASSET_TABLE}.ZUUID = '{uuid}'
+ """
+ with sqlite3.connect(self.db_path) as conn:
+ c = conn.cursor()
+ c.execute(sql)
+ results = c.fetchone()
+ tz, tzname = (results[0], results[1])
+ tz_str = tz_to_str(tz)
+ return tz, tz_str, tzname
+
+
+class PhotoTimeZoneUpdater:
+ """Update timezones for Photos objects"""
+
+ def __init__(
+ self,
+ timezone: Timezone,
+ verbose: Optional[Callable] = None,
+ library_path: Optional[str] = None,
+ ):
+ self.timezone = timezone
+ self.tz_offset = timezone.offset
+ self.tz_name = timezone.name
+
+ self.verbose = verbose or noop
+
+ # get_last_library_path() returns the path to the last Photos library
+ # opened but sometimes (rarely) fails on some systems
+ try:
+ db_path = (
+ library_path or get_last_library_path() or get_system_library_path()
+ )
+ except Exception:
+ db_path = None
+ if not db_path:
+ raise FileNotFoundError("Could not find Photos database path")
+
+ photos_version = get_photos_library_version(db_path)
+ db_path = str(pathlib.Path(db_path) / "database/Photos.sqlite")
+ self.db_path = db_path
+ self.ASSET_TABLE = _DB_TABLE_NAMES[photos_version]["ASSET"]
+
+ def update_photo(self, photo: Photo):
+ """Update the timezone of a photo in the database
+
+ Args:
+ photo: Photo object to update
+ """
+ try:
+ self._update_photo(photo)
+ except Exception as e:
+ self.verbose(f"Error updating {photo.uuid}: {e}")
+
+ @retry(
+ wait=wait_exponential(multiplier=1, min=0.100, max=5),
+ stop=stop_after_attempt(10),
+ )
+ def _update_photo(self, photo: Photo):
+ try:
+ uuid = photo.uuid
+ sql = f""" SELECT
+ ZADDITIONALASSETATTRIBUTES.Z_PK,
+ ZADDITIONALASSETATTRIBUTES.Z_OPT,
+ ZADDITIONALASSETATTRIBUTES.ZTIMEZONEOFFSET,
+ ZADDITIONALASSETATTRIBUTES.ZTIMEZONENAME
+ FROM ZADDITIONALASSETATTRIBUTES
+ JOIN {self.ASSET_TABLE}
+ ON ZADDITIONALASSETATTRIBUTES.ZASSET = {self.ASSET_TABLE}.Z_PK
+ WHERE {self.ASSET_TABLE}.ZUUID = '{uuid}'
+ """
+ with sqlite3.connect(self.db_path) as conn:
+ c = conn.cursor()
+ c.execute(sql)
+ results = c.fetchone()
+ z_opt = results[1] + 1
+ z_pk = results[0]
+ tz_offset = results[2]
+ tz_name = results[3]
+ sql_update = f""" UPDATE ZADDITIONALASSETATTRIBUTES
+ SET Z_OPT={z_opt},
+ ZTIMEZONEOFFSET={self.tz_offset},
+ ZTIMEZONENAME='{self.tz_name}'
+ WHERE Z_PK={z_pk};
+ """
+ with sqlite3.connect(self.db_path) as conn:
+ c = conn.cursor()
+ c.execute(sql_update)
+ conn.commit()
+ self.verbose(
+ f"Updated timezone for photo [filename]{photo.filename}[/filename] ([uuid]{photo.uuid}[/uuid]) "
+ + f"from [tz]{[tz_name]}[/tz], offset=[tz]{tz_offset}[/tz] "
+ + f"to [tz]{self.tz_name}[/tz], offset=[tz]{self.tz_offset}[/tz]"
+ )
+ except Exception as e:
+ raise e
diff --git a/osxphotos/timeutils.py b/osxphotos/timeutils.py
new file mode 100644
index 00000000..86916893
--- /dev/null
+++ b/osxphotos/timeutils.py
@@ -0,0 +1,84 @@
+"""Utilities for working with datetimes"""
+
+import datetime
+from typing import Optional
+
+import re
+
+
+def utc_offset_string_to_seconds(utc_offset: str) -> int:
+ """match a UTC offset in format ±[hh]:[mm], ±[h]:[mm], or ±[hh][mm] and return number of seconds offset"""
+ patterns = ["^([+-]?)(\d{1,2}):(\d{2})$", "^([+-]?)(\d{2})(\d{2})$"]
+ for pattern in patterns:
+ match = re.match(pattern, utc_offset)
+ if not match:
+ continue
+ sign = match[1]
+ hours = int(match[2])
+ minutes = int(match[3])
+ if sign == "-":
+ hours = -hours
+ minutes = -minutes
+ return (hours * 60 + minutes) * 60
+ raise ValueError(f"Invalid UTC offset format: {utc_offset}.")
+
+
+def update_datetime(
+ dt: datetime.datetime,
+ date: Optional[datetime.date] = None,
+ time: Optional[datetime.time] = None,
+ date_delta: Optional[datetime.timedelta] = None,
+ time_delta: Optional[datetime.timedelta] = None,
+) -> datetime.datetime:
+ """
+ Update the date and time of a datetime object.
+
+ Args:
+ dt: datetime object
+ date: new date
+ time: new time
+ date_delta: a timedelta to apply
+ time_delta: a timedelta to apply
+ """
+ if date is not None:
+ dt = dt.replace(year=date.year, month=date.month, day=date.day)
+ if time is not None:
+ dt = dt.replace(
+ hour=time.hour,
+ minute=time.minute,
+ second=time.second,
+ microsecond=time.microsecond,
+ )
+ if date_delta is not None:
+ dt = dt + date_delta
+ if time_delta is not None:
+ dt = dt + time_delta
+ return dt
+
+
+def time_string_to_datetime(time: str) -> datetime.datetime:
+ """Convert time string to datetime.datetime"""
+
+ """ valid time formats:
+ - HH:MM:SS,
+ - HH:MM:SS.fff,
+ - HH:MM,
+
+ """
+
+ time_formats = [
+ "%H:%M:%S",
+ "%H:%M:%S.%f",
+ "%H:%M",
+ ]
+
+ for dt_format in time_formats:
+ try:
+ parsed_dt = datetime.datetime.strptime(time, dt_format)
+ except ValueError as e:
+ pass
+ else:
+ return parsed_dt
+ raise ValueError(
+ f"Could not parse time format: {time} does not match {time_formats}"
+ )
diff --git a/osxphotos/timezones.py b/osxphotos/timezones.py
new file mode 100644
index 00000000..42029e7b
--- /dev/null
+++ b/osxphotos/timezones.py
@@ -0,0 +1,56 @@
+"""Get list of valid timezones on macOS"""
+
+from typing import Union
+
+import Foundation
+import objc
+
+
+def known_timezone_names():
+ """Get list of valid timezones on macOS"""
+ return Foundation.NSTimeZone.knownTimeZoneNames()
+
+
+def format_offset_time(offset: int) -> str:
+ """Format offset time to exiftool format: -04:00"""
+ sign = "-" if offset < 0 else "+"
+ hours, remainder = divmod(abs(offset), 3600)
+ minutes, _ = divmod(remainder, 60)
+ return f"{sign}{hours:02d}:{minutes:02d}"
+
+
+class Timezone:
+ """Create Timezone object from either name (str) or offset from GMT (int)"""
+
+ def __init__(self, tz: Union[str, int]):
+ with objc.autorelease_pool():
+ if isinstance(tz, str):
+ self.timezone = Foundation.NSTimeZone.timeZoneWithName_(tz)
+ self._name = tz
+ elif isinstance(tz, int):
+ self.timezone = Foundation.NSTimeZone.timeZoneForSecondsFromGMT_(tz)
+ self._name = self.timezone.name()
+ else:
+ raise TypeError("Timezone must be a string or an int")
+
+ @property
+ def name(self) -> str:
+ return self._name
+
+ @property
+ def offset(self) -> int:
+ return self.timezone.secondsFromGMT()
+
+ @property
+ def offset_str(self) -> str:
+ return format_offset_time(self.offset)
+
+ @property
+ def abbreviation(self) -> str:
+ return self.timezone.abbreviation()
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return self.name
diff --git a/osxphotos/utils.py b/osxphotos/utils.py
index fb9ec25e..da100c58 100644
--- a/osxphotos/utils.py
+++ b/osxphotos/utils.py
@@ -38,6 +38,7 @@ __all__ = [
"noop",
"normalize_fs_path",
"normalize_unicode",
+ "pluralize",
]
@@ -511,3 +512,8 @@ def get_latest_version() -> Tuple[Optional[str], str]:
return data["info"]["version"], ""
except Exception as e:
return None, e
+
+
+def pluralize(count, singular, plural):
+ """Return singular or plural based on count"""
+ return singular if count == 1 else plural
diff --git a/requirements.txt b/requirements.txt
index 47041fe0..857ba985 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,10 +19,11 @@ pyobjc-framework-Metal>=7.3,<9.0
pyobjc-framework-Photos>=7.3,<9.0
pyobjc-framework-Quartz>=7.3,<9.0
pyobjc-framework-Vision>=7.3,<9.0
+pytimeparse==1.1.8
PyYAML>=5.4.1,<6.0.0
requests>=2.27.1,<3.0.0
rich>=11.2.0,<13.0.0
-rich_theme_manager>=0.9.0
+rich_theme_manager>=0.11.0
tenacity>=8.0.1,<9.0.0
textx>=2.3.0,<2.4.0
toml>=0.10.2,<0.11.0
diff --git a/setup.py b/setup.py
index e9d2207b..2ae37b37 100755
--- a/setup.py
+++ b/setup.py
@@ -95,9 +95,10 @@ setup(
"pyobjc-framework-Photos>=7.3,<9.0",
"pyobjc-framework-Quartz>=7.3,<9.0",
"pyobjc-framework-Vision>=7.3,<9.0",
+ "pytimeparse==1.1.8",
"requests>=2.27.1,<3.0.0",
"rich>=11.2.0,<13.0.0",
- "rich_theme_manager>=0.9.0",
+ "rich_theme_manager>=0.11.0",
"tenacity>=8.0.1,<9.0.0",
"textx>=2.3.0,<3.0.0",
"toml>=0.10.2,<0.11.0",
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/DataModelVersion.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/DataModelVersion.plist
new file mode 100644
index 00000000..be9740fe
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/database/DataModelVersion.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ LibrarySchemaVersion
+ 5001
+ MetaSchemaVersion
+ 3
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite
new file mode 100644
index 00000000..20f8edee
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-shm
new file mode 100644
index 00000000..89145be6
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-wal
new file mode 100644
index 00000000..3862fe84
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite.lock b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite.lock
new file mode 100644
index 00000000..c00c434a
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/database/Photos.sqlite.lock
@@ -0,0 +1,16 @@
+
+
+
+
+ hostname
+ Rhets-MacBook-Pro.local
+ hostuuid
+ 585B80BF-8D1F-55EF-A9E8-6CF4E5523959
+ pid
+ 1961
+ processname
+ photolibraryd
+ uid
+ 501
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/metaSchema.db b/tests/TestTimeWarp-10.15.7.photoslibrary/database/metaSchema.db
new file mode 100644
index 00000000..2d75bd40
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/metaSchema.db differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/photos.db b/tests/TestTimeWarp-10.15.7.photoslibrary/database/photos.db
new file mode 100644
index 00000000..2d75bd40
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/photos.db differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/protection b/tests/TestTimeWarp-10.15.7.photoslibrary/database/protection
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/graphDataProgress.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/graphDataProgress.plist
new file mode 100644
index 00000000..c4e5e427
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/graphDataProgress.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite
new file mode 100644
index 00000000..4680ee4c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite-shm
new file mode 100644
index 00000000..780dd2ab
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/psi.sqlite-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchMetadata.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchMetadata.plist
new file mode 100644
index 00000000..3eec4180
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchMetadata.plist
@@ -0,0 +1,188 @@
+
+
+
+
+ BlacklistedMeaningsByMeaning
+
+ MePersonUUID
+ 39488755-78C0-40B2-B378-EDA280E1823C
+ SceneWhitelist
+
+ Graduation
+ Aquarium
+ Food
+ Ice Skating
+ Mountain
+ Cliff
+ Basketball
+ Tennis
+ Jewelry
+ Cheese
+ Softball
+ Football
+ Circus
+ Jet Ski
+ Playground
+ Carousel
+ Paint Ball
+ Windsurfing
+ Sailboat
+ Sunbathing
+ Dam
+ Fireplace
+ Flower
+ Scuba
+ Hiking
+ Cetacean
+ Pier
+ Bowling
+ Snowboarding
+ Zoo
+ Snowmobile
+ Theater
+ Boat
+ Casino
+ Car
+ Diving
+ Cycling
+ Musical Instrument
+ Board Game
+ Castle
+ Sunset Sunrise
+ Martial Arts
+ Motocross
+ Submarine
+ Cat
+ Snow
+ Kiteboarding
+ Squash
+ Geyser
+ Music
+ Archery
+ Desert
+ Blackjack
+ Fireworks
+ Sportscar
+ Feline
+ Soccer
+ Museum
+ Baby
+ Fencing
+ Railroad
+ Nascar
+ Sky Surfing
+ Bird
+ Games
+ Baseball
+ Dressage
+ Snorkeling
+ Pyramid
+ Kite
+ Rowboat
+ Golf
+ Watersports
+ Lightning
+ Canyon
+ Auditorium
+ Night Sky
+ Karaoke
+ Skiing
+ Parade
+ Forest
+ Hot Air Balloon
+ Dragon Parade
+ Easter Egg
+ Monument
+ Jungle
+ Thanksgiving
+ Jockey Horse
+ Stadium
+ Airplane
+ Ballet
+ Yoga
+ Coral Reef
+ Skating
+ Wrestling
+ Bicycle
+ Tattoo
+ Amusement Park
+ Canoe
+ Cheerleading
+ Ping Pong
+ Fishing
+ Magic
+ Reptile
+ Winter Sport
+ Waterfall
+ Train
+ Bonsai
+ Surfing
+ Dog
+ Cake
+ Sledding
+ Sandcastle
+ Glacier
+ Lighthouse
+ Equestrian
+ Rafting
+ Shore
+ Hockey
+ Santa Claus
+ Formula One Car
+ Sport
+ Vehicle
+ Boxing
+ Rollerskating
+ Underwater
+ Orchestra
+ Carnival
+ Rocket
+ Skateboarding
+ Helicopter
+ Performance
+ Oktoberfest
+ Water Polo
+ Skate Park
+ Animal
+ Nightclub
+ String Instrument
+ Dinosaur
+ Gymnastics
+ Cricket
+ Volcano
+ Lake
+ Aurora
+ Dancing
+ Concert
+ Rock Climbing
+ Hang Glider
+ Rodeo
+ Fish
+ Art
+ Motorcycle
+ Volleyball
+ Wake Boarding
+ Badminton
+ Motor Sport
+ Sumo
+ Parasailing
+ Skydiving
+ Kickboxing
+ Pinata
+ Foosball
+ Go Kart
+ Poker
+ Kayak
+ Swimming
+ Atv
+ Beach
+ Dartboard
+ Athletics
+ Camping
+ Tornado
+ Billiards
+ Rugby
+ Airshow
+
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchProgress.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchProgress.plist
new file mode 100644
index 00000000..ad779080
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchProgress.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ insertAlbum
+
+ insertAsset
+
+ insertHighlight
+
+ insertMemory
+
+ insertMoment
+
+ removeAlbum
+
+ removeAsset
+
+ removeHighlight
+
+ removeMemory
+
+ removeMoment
+
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchSystemInfo.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchSystemInfo.plist
new file mode 100644
index 00000000..f6d69ef7
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/searchSystemInfo.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ embeddingVersion
+ 1
+ localeIdentifier
+ en_US
+ sceneTaxonomySHA
+ 87914a047c69fbe8013fad2c70fa70c6c03b08b56190fe4054c880e6b9f57cc3
+ searchIndexVersion
+ 10
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/synonymsProcess.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/synonymsProcess.plist
new file mode 100644
index 00000000..f5ebcace
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/synonymsProcess.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/zeroKeywords.data b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/zeroKeywords.data
new file mode 100644
index 00000000..cb4f7e5e
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/database/search/zeroKeywords.data differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.mov b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.mov
new file mode 100644
index 00000000..25880d7d
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.mov differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E.jpeg
new file mode 100644
index 00000000..6154a7af
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A.jpeg
new file mode 100644
index 00000000..e8d99c07
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9.jpeg
new file mode 100644
index 00000000..932b4ca8
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/C/C4D952AF-983D-438E-9070-6310B1BC4826.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/C/C4D952AF-983D-438E-9070-6310B1BC4826.jpeg
new file mode 100644
index 00000000..4d6d839b
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/C/C4D952AF-983D-438E-9070-6310B1BC4826.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/originals/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2.jpeg
new file mode 100644
index 00000000..6d3ced22
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/originals/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.Photos/appPrivateData.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.Photos/appPrivateData.plist
new file mode 100644
index 00000000..08370bf1
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.Photos/appPrivateData.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ CollapsedSidebarSectionIdentifiers
+
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite
new file mode 100644
index 00000000..1c6ab69d
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm
new file mode 100644
index 00000000..7c8bf618
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal
new file mode 100644
index 00000000..94274e11
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite
new file mode 100644
index 00000000..89e74fff
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm
new file mode 100644
index 00000000..f8e7976c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal
new file mode 100644
index 00000000..1dc472c7
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite
new file mode 100644
index 00000000..e2e02583
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite
new file mode 100644
index 00000000..53cf961d
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm
new file mode 100644
index 00000000..6cea8184
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal
new file mode 100644
index 00000000..88b5daa5
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite
new file mode 100644
index 00000000..0c14ec5a
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite
new file mode 100644
index 00000000..b65eb008
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm
new file mode 100644
index 00000000..319c2669
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal
new file mode 100644
index 00000000..77e45067
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite
new file mode 100644
index 00000000..d99c5ca9
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite
new file mode 100644
index 00000000..4ab71e6c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm
new file mode 100644
index 00000000..44e3880e
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal
new file mode 100644
index 00000000..99584c2b
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist
new file mode 100644
index 00000000..0ffe7e55
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSharingFeatureExtractorRecords.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSharingFeatureExtractorRecords.plist
new file mode 100644
index 00000000..0de953ba
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSharingFeatureExtractorRecords.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
new file mode 100644
index 00000000..cb636d77
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ BackgroundHighlightCollection
+ 2021-10-09T17:36:25Z
+ BackgroundHighlightEnrichment
+ 2021-10-09T17:36:25Z
+ BackgroundJobAssetRevGeocode
+ 2021-10-09T17:36:25Z
+ BackgroundJobSearch
+ 2021-10-09T17:36:25Z
+ BackgroundPeopleSuggestion
+ 2021-10-09T17:36:24Z
+ BackgroundUserBehaviorProcessor
+ 2021-10-07T03:30:30Z
+ PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey
+ 2021-10-07T03:30:29Z
+ PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate
+ 2021-10-07T03:30:29Z
+ PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate
+ 2021-10-09T17:36:25Z
+ SiriPortraitDonation
+ 2021-10-08T20:51:05Z
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb
new file mode 100644
index 00000000..4ab71e6c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb
new file mode 100644
index 00000000..4ab71e6c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/liveupdate-photosgraph.kgdb-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb
new file mode 100644
index 00000000..4ab71e6c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm
new file mode 100644
index 00000000..fe9ac284
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb
new file mode 100644
index 00000000..5923841c
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb-shm b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb-shm
new file mode 100644
index 00000000..fef44e72
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb-shm differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb-wal b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb-wal
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/changetoken.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/changetoken.plist
new file mode 100644
index 00000000..3ad62b38
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/changetoken.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/revgeoprovider.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/revgeoprovider.plist
new file mode 100644
index 00000000..bf8f1e5d
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photoanalysisd/caches/graph/revgeoprovider.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ revgeoprovider
+ 7618
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photolibraryd/appPrivateData.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photolibraryd/appPrivateData.plist
new file mode 100644
index 00000000..2120d8ee
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photolibraryd/appPrivateData.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ PLLibraryServicesManager.LocaleIdentifier
+ en_US
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photolibraryd/caches/CreateDatabase_20211006-202841-07:00 b/tests/TestTimeWarp-10.15.7.photoslibrary/private/com.apple.photolibraryd/caches/CreateDatabase_20211006-202841-07:00
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.THM b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.THM
new file mode 100644
index 00000000..b4059477
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12.THM differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_1_102_o.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_1_102_o.jpeg
new file mode 100644
index 00000000..446c6af0
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_1_102_o.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_1_105_c.jpeg
new file mode 100644
index 00000000..45d35711
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_1_105_c.jpeg
new file mode 100644
index 00000000..52973217
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/5/53615D56-91F7-4908-81F1-B93B5DEA7449_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/5/53615D56-91F7-4908-81F1-B93B5DEA7449_1_105_c.jpeg
new file mode 100644
index 00000000..fb8dbeda
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/5/53615D56-91F7-4908-81F1-B93B5DEA7449_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_1_105_c.jpeg
new file mode 100644
index 00000000..cefd4f88
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/C/C4D952AF-983D-438E-9070-6310B1BC4826_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/C/C4D952AF-983D-438E-9070-6310B1BC4826_1_105_c.jpeg
new file mode 100644
index 00000000..b5976ec6
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/C/C4D952AF-983D-438E-9070-6310B1BC4826_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_1_105_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_1_105_c.jpeg
new file mode 100644
index 00000000..9a43c7a6
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_1_105_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_4_5005_c.jpeg
new file mode 100644
index 00000000..4976d9bd
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/16BEC0BE-4188-44F1-A8F1-7250E978AD12_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_4_5005_c.jpeg
new file mode 100644
index 00000000..f4394751
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/1/1A61156A-5747-42DE-A9B3-4A468CC49D9E_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_4_5005_c.jpeg
new file mode 100644
index 00000000..49bc1d93
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/2/2F00448D-3C0D-477A-9B10-5F21DCAB405A_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/5/53615D56-91F7-4908-81F1-B93B5DEA7449_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/5/53615D56-91F7-4908-81F1-B93B5DEA7449_4_5005_c.jpeg
new file mode 100644
index 00000000..a5931e47
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/5/53615D56-91F7-4908-81F1-B93B5DEA7449_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_4_5005_c.jpeg
new file mode 100644
index 00000000..c7604132
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/7/7E9DF2EE-A5B0-4077-80EC-30565221A3B9_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/C/C4D952AF-983D-438E-9070-6310B1BC4826_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/C/C4D952AF-983D-438E-9070-6310B1BC4826_4_5005_c.jpeg
new file mode 100644
index 00000000..3b6432e4
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/C/C4D952AF-983D-438E-9070-6310B1BC4826_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_4_5005_c.jpeg b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_4_5005_c.jpeg
new file mode 100644
index 00000000..8b176871
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/masters/F/FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2_4_5005_c.jpeg differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/3305.ithmb b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/3305.ithmb
new file mode 100644
index 00000000..da0f3f97
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/3305.ithmb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4031.ithmb b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4031.ithmb
new file mode 100644
index 00000000..ec419618
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4031.ithmb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4132.ithmb b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4132.ithmb
new file mode 100644
index 00000000..45b3c99f
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/4132.ithmb differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/thumbnailConfiguration b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/thumbnailConfiguration
new file mode 100644
index 00000000..8c122be1
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/derivatives/thumbs/thumbnailConfiguration
@@ -0,0 +1,10 @@
+
+
+
+
+ PLThumbnailManagerThumbnailFormatKey
+ 5005
+ PLThumbnailManagerVersionKey
+ 28
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Album-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Album-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Album.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Album.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Album.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset-change.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset-change.plj
new file mode 100644
index 00000000..4b109c3f
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset-change.plj differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset.plist
new file mode 100644
index 00000000..053537c0
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Asset.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 10
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 10
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DeferredRebuildFace-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DeferredRebuildFace-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DeferredRebuildFace.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DeferredRebuildFace.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DeferredRebuildFace.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DetectedFace-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DetectedFace-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DetectedFace.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DetectedFace.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/DetectedFace.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FetchingAlbum-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FetchingAlbum-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FetchingAlbum.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FetchingAlbum.plist
new file mode 100644
index 00000000..59390bdc
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FetchingAlbum.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 2
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 2
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FileSystemVolume-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FileSystemVolume-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FileSystemVolume.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FileSystemVolume.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/FileSystemVolume.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder-snapshot.plj
new file mode 100644
index 00000000..e7f358d3
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder-snapshot.plj differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Folder.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/HistoryToken.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/HistoryToken.plist
new file mode 100644
index 00000000..eea641bc
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/HistoryToken.plist differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession-change.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession-change.plj
new file mode 100644
index 00000000..ccfbf877
Binary files /dev/null and b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession-change.plj differ
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ImportSession.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Keyword-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Keyword-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Keyword.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Keyword.plist
new file mode 100644
index 00000000..bb608f4f
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Keyword.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:44Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Memory-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Memory-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Memory.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Memory.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Memory.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Person-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Person-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Person.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Person.plist
new file mode 100644
index 00000000..a86ede0b
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/Person.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:45Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ProjectAlbum-snapshot.plj b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ProjectAlbum-snapshot.plj
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ProjectAlbum.plist b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ProjectAlbum.plist
new file mode 100644
index 00000000..bb608f4f
--- /dev/null
+++ b/tests/TestTimeWarp-10.15.7.photoslibrary/resources/journals/ProjectAlbum.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ currentPayloadVersion
+ 1
+ snapshotDate
+ 2021-10-07T03:28:44Z
+ snapshotPayloadVersion
+ 1
+
+
diff --git a/tests/config_timewarp_catalina.py b/tests/config_timewarp_catalina.py
new file mode 100644
index 00000000..2b3444bd
--- /dev/null
+++ b/tests/config_timewarp_catalina.py
@@ -0,0 +1,342 @@
+""" Test data for timewarp command on Catalina/Photos 5 """
+
+import datetime
+
+from tests.parse_timewarp_output import CompareValues, InspectValues
+
+TEST_LIBRARY_TIMEWARP = "tests/TestTimeWarp-10.15.7.photoslibrary"
+
+CATALINA_PHOTOS_5 = {
+ "filenames": {
+ "pumpkins": "IMG_6522.jpeg",
+ "pears": "IMG_6501.jpeg",
+ "sunflowers": "IMG_6520.jpeg",
+ "apple tree": "IMG_6526.jpeg",
+ "marigold flowers": "IMG_6517.jpeg",
+ "multi-colored zinnia flowers": "IMG_6506.jpeg",
+ "sunset": "IMG_6551.mov",
+ },
+ "inspect": {
+ # IMG_6501.jpeg
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "expected": [
+ InspectValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2021-10-02 12:40:07-0700",
+ "2021-10-02 12:40:07-0700",
+ "-0700",
+ "GMT-0700",
+ )
+ ],
+ },
+ "date": {
+ # IMG_6501.jpeg
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "value": "2020-09-01",
+ "date": datetime.datetime(2020, 9, 1, 12, 40, 7),
+ },
+ "date_delta": {
+ # IMG_6501.jpeg
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "parameters": [
+ ("1", "2020-09-02 12:40:07-0700"),
+ ("1 day", "2020-09-03 12:40:07-0700"),
+ ("1 week", "2020-09-10 12:40:07-0700"),
+ ("-1", "2020-09-09 12:40:07-0700"),
+ ("-1 day", "2020-09-08 12:40:07-0700"),
+ ("-1 week", "2020-09-01 12:40:07-0700"),
+ ],
+ },
+ "time": {
+ # IMG_6501.jpeg
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "parameters": [
+ ("14:42", "2020-09-01 14:42:00-0700"),
+ ("14:42:30", "2020-09-01 14:42:30-0700"),
+ # Photos doesn't return the milliseconds
+ ("14:42:31.234", "2020-09-01 14:42:31-0700"),
+ ],
+ },
+ "time_delta": {
+ # IMG_6501.jpeg
+ # Format is one of '±HH:MM:SS', '±H hours' (or hr), '±M minutes' (or min), '±S seconds' (or sec), '±S'(where S is seconds)
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "parameters": [
+ ("1:10:20", "2020-09-01 15:52:51-0700"),
+ ("1 hours", "2020-09-01 16:52:51-0700"),
+ ("1", "2020-09-01 16:52:52-0700"),
+ ("+1", "2020-09-01 16:52:53-0700"),
+ ("-1", "2020-09-01 16:52:52-0700"),
+ ("-1 hour", "2020-09-01 15:52:52-0700"),
+ ("3 minutes", "2020-09-01 15:55:52-0700"),
+ ("3 min", "2020-09-01 15:58:52-0700"),
+ ("-6 min", "2020-09-01 15:52:52-0700"),
+ ("+10 sec", "2020-09-01 15:53:02-0700"),
+ ],
+ },
+ "time_zone": {
+ # IMG_6501.jpeg
+ # Format is one of '±HH:MM', '±H:MM', or '±HHMM'
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "parameters": [
+ ("-06:00", "2020-09-01 16:53:02-0600", "-0600"),
+ ],
+ },
+ "compare_exif": {
+ # IMG_6501.jpeg
+ # filename, uuid, photo time (Photos), photo time (EXIF), timezone offset (Photos), timezone offset (EXIF)
+ # IMG_6501.jpeg, 2F00448D-3C0D-477A-9B10-5F21DCAB405A, 2020-09-01 16:53:02, 2021-10-02 12:40:07, -0600, -0700
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "expected": [
+ CompareValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2020-09-01 16:53:02",
+ "2021-10-02 12:40:07",
+ "-0600",
+ "-0700",
+ ),
+ ],
+ },
+ "compare_exif_add_to_album": {
+ # IMG_6501.jpeg
+ "uuid": "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "expected": [("found 1 that is different", "Different EXIF")],
+ },
+ "compare_exif_3": {
+ # IMG_6520.jpeg
+ # IMG_6520.jpeg, 53615D56-91F7-4908-81F1-B93B5DEA7449, 2021-10-02 12:54:36, , -0700,
+ "uuid": "53615D56-91F7-4908-81F1-B93B5DEA7449",
+ "expected": [
+ CompareValues(
+ "IMG_6520.jpeg",
+ "53615D56-91F7-4908-81F1-B93B5DEA7449",
+ "2021-10-02 12:54:36",
+ "",
+ "-0700",
+ "",
+ ),
+ ],
+ },
+ "match": { # IMG_6520.jpeg
+ # IMG_6520.jpeg, 53615D56-91F7-4908-81F1-B93B5DEA7449, 2021-10-02 12:54:36, , -0700,
+ "uuid": "53615D56-91F7-4908-81F1-B93B5DEA7449",
+ "parameters": [
+ ("-0500", "2021-10-02 12:54:36-0500"),
+ ("+01:00", "2021-10-02 12:54:36+0100"),
+ ],
+ },
+ "exiftool": {
+ # IMG_6522.jpeg
+ "uuid": "FD1E3A36-3E65-48AF-9B14-DCFF65A9D3D2",
+ # match,tz_value,time_delta_value,expected_date,exif_date,exif_offset
+ "parameters": [
+ (
+ True,
+ "-0300",
+ "+1 hour",
+ "2021-10-02 13:56:11-0300",
+ "2021:10:02 13:56:11",
+ "-03:00",
+ ),
+ (
+ False,
+ "-0400",
+ "+2 hours",
+ "2021-10-02 14:56:11-0400",
+ "2021:10:02 14:56:11",
+ "-04:00",
+ ),
+ ],
+ },
+ "push_exif": {
+ # IMG_6501.jpeg
+ "pre": CompareValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2020-09-01 16:53:02",
+ "2021-10-02 12:40:07",
+ "-0600",
+ "-0700",
+ ),
+ "post": CompareValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2020-09-01 16:53:02",
+ "2020-09-01 16:53:02",
+ "-0600",
+ "-0600",
+ ),
+ },
+ "pull_exif_1": {
+ # IMG_6501.jpeg
+ "pre": CompareValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2020-09-02 16:53:02",
+ "2020-09-01 16:53:02",
+ "-0400",
+ "-0600",
+ ),
+ "post": CompareValues(
+ "IMG_6501.jpeg",
+ "2F00448D-3C0D-477A-9B10-5F21DCAB405A",
+ "2020-09-01 16:53:02",
+ "2020-09-01 16:53:02",
+ "-0600",
+ "-0600",
+ ),
+ },
+ "pull_exif_no_time": {
+ # IMG_6526.jpeg (apple tree)
+ "pre": CompareValues(
+ "IMG_6526.jpeg",
+ "1A61156A-5747-42DE-A9B3-4A468CC49D9E",
+ "2021-10-02 15:15:00",
+ "2021-10-02 00:00:00",
+ "-0400",
+ "-0700",
+ ),
+ "post": CompareValues(
+ "IMG_6526.jpeg",
+ "1A61156A-5747-42DE-A9B3-4A468CC49D9E",
+ "2021-10-02 00:00:00",
+ "2021-10-02 00:00:00",
+ "-0700",
+ "-0700",
+ ),
+ },
+ "pull_exif_no_offset": {
+ # IMG_6517.jpeg
+ "pre": CompareValues(
+ "IMG_6517.jpeg",
+ "C4D952AF-983D-438E-9070-6310B1BC4826",
+ "2021-10-02 12:50:00",
+ "2021-10-02 12:51:15",
+ "-0700",
+ "",
+ ),
+ "post": CompareValues(
+ "IMG_6517.jpeg",
+ "C4D952AF-983D-438E-9070-6310B1BC4826",
+ "2021-10-02 12:51:15",
+ "2021-10-02 12:51:15",
+ "-0700",
+ "",
+ ),
+ },
+ "pull_exif_no_data": {
+ # IMG_6506.jpeg (zinnia flowers)
+ "pre": CompareValues(
+ "IMG_6506.jpeg",
+ "7E9DF2EE-A5B0-4077-80EC-30565221A3B9",
+ "2021-10-08 16:11:09",
+ "",
+ "-0700",
+ "",
+ ),
+ "post": CompareValues(
+ "IMG_6506.jpeg",
+ "7E9DF2EE-A5B0-4077-80EC-30565221A3B9",
+ "2021-10-08 16:11:09",
+ "",
+ "-0700",
+ "",
+ ),
+ },
+ "pull_exif_no_data_use_file_time": {
+ # IMG_6506.jpeg (zinnia flowers)
+ "pre": CompareValues(
+ "IMG_6506.jpeg",
+ "7E9DF2EE-A5B0-4077-80EC-30565221A3B9",
+ "2021-10-08 16:11:09",
+ "",
+ "-0700",
+ "",
+ ),
+ "post": CompareValues(
+ "IMG_6506.jpeg",
+ "7E9DF2EE-A5B0-4077-80EC-30565221A3B9",
+ "2021-10-08 16:11:09",
+ "",
+ "-0700",
+ "",
+ ),
+ },
+ "compare_video_1": {
+ # IMG_6551.mov
+ "expected": [
+ CompareValues(
+ "IMG_6551.mov",
+ "16BEC0BE-4188-44F1-A8F1-7250E978AD12",
+ "2021-10-04 19:01:03",
+ "2021-10-04 19:01:03",
+ "-0700",
+ "-0700",
+ )
+ ]
+ },
+ "video_date_delta": {
+ # IMG_6551.mov
+ "parameters": [("-1 day", "2021-10-03 19:01:03-0700")]
+ },
+ "video_time_delta": {
+ # IMG_6551.mov
+ "parameters": [("+1 hour", "2021-10-03 20:01:03-0700")]
+ },
+ "video_date": {
+ # IMG_6551.mov
+ "parameters": [("2021-10-04", "2021-10-04 20:01:03-0700")]
+ },
+ "video_time": {
+ # IMG_6551.mov
+ "parameters": [("20:00:00", "2021-10-04 20:00:00-0700")]
+ },
+ "video_time_zone": {
+ # IMG_6551.mov
+ "parameters": [("-0400", "2021-10-04 23:00:00-0400", "-0400")]
+ },
+ "video_match": {
+ # IMG_6551.mov
+ "parameters": [("-0200", "2021-10-04 23:00:00-0200")]
+ },
+ "video_push_exif": {
+ # IMG_6501.jpeg
+ "pre": CompareValues(
+ "IMG_6551.mov",
+ "16BEC0BE-4188-44F1-A8F1-7250E978AD12",
+ "2021-10-04 23:00:00",
+ "2021-10-04 19:01:03",
+ "-0200",
+ "-0700",
+ ),
+ "post": CompareValues(
+ "IMG_6551.mov",
+ "16BEC0BE-4188-44F1-A8F1-7250E978AD12",
+ "2021-10-04 23:00:00",
+ "2021-10-04 23:00:00",
+ "-0200",
+ "-0200",
+ ),
+ },
+ "video_pull_exif": {
+ # IMG_6501.jpeg
+ "pre": CompareValues(
+ "IMG_6551.mov",
+ "16BEC0BE-4188-44F1-A8F1-7250E978AD12",
+ "2021-10-05 13:00:00",
+ "2021-10-04 23:00:00",
+ "-0500",
+ "-0200",
+ ),
+ "post": CompareValues(
+ "IMG_6551.mov",
+ "16BEC0BE-4188-44F1-A8F1-7250E978AD12",
+ "2021-10-04 23:00:00",
+ "2021-10-04 23:00:00",
+ "-0200",
+ "-0200",
+ ),
+ },
+}
diff --git a/tests/conftest.py b/tests/conftest.py
index a16a8045..dc125675 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,7 +1,9 @@
""" pytest test configuration """
import os
import pathlib
+import tempfile
+import photoscript
import pytest
from applescript import AppleScript
from photoscript.utils import ditto
@@ -10,6 +12,8 @@ from osxphotos.exiftool import _ExifToolProc
from .test_catalina_10_15_7 import UUID_DICT_LOCAL
+TEST_TIMEWARP = False
+
def get_os_version():
import platform
@@ -34,11 +38,20 @@ def get_os_version():
OS_VER = get_os_version()[1]
if OS_VER == "15":
TEST_LIBRARY = "tests/Test-10.15.7.photoslibrary"
+ from tests.config_timewarp_catalina import TEST_LIBRARY_TIMEWARP
else:
TEST_LIBRARY = None
+ TEST_LIBRARY_TIMEWARP = None
# pytest.exit("This test suite currently only runs on MacOS Catalina ")
+@pytest.fixture(scope="session", autouse=True)
+def setup_photos_timewarp():
+ if not TEST_TIMEWARP:
+ return
+ copy_photos_library(TEST_LIBRARY_TIMEWARP, delay=10)
+
+
@pytest.fixture(autouse=True)
def reset_singletons():
"""Need to clean up any ExifTool singletons between tests"""
@@ -52,28 +65,46 @@ def pytest_addoption(parser):
default=False,
help="run --add-exported-to-album tests",
)
+ parser.addoption(
+ "--timewarp", action="store_true", default=False, help="run --timewarp tests"
+ )
def pytest_configure(config):
+ if config.getoption("--addalbum") and config.getoption("--timewarp"):
+ pytest.exit("--addalbum and --timewarp are mutually exclusive")
+
config.addinivalue_line(
"markers", "addalbum: mark test as requiring --addalbum to run"
)
+ config.addinivalue_line(
+ "markers", "timewarp: mark test as requiring --timewarp to run"
+ )
+
+ if config.getoption("--timewarp"):
+ global TEST_TIMEWARP
+ TEST_TIMEWARP = True
def pytest_collection_modifyitems(config, items):
- if config.getoption("--addalbum") and TEST_LIBRARY is not None:
- # --addalbum given in cli: do not skip addalbum tests (these require interactive test)
- return
+ if not (config.getoption("--addalbum") and TEST_LIBRARY is not None):
+ skip_addalbum = pytest.mark.skip(
+ reason="need --addalbum option and MacOS Catalina to run"
+ )
+ for item in items:
+ if "addalbum" in item.keywords:
+ item.add_marker(skip_addalbum)
- skip_addalbum = pytest.mark.skip(
- reason="need --addalbum option and MacOS Catalina to run"
- )
- for item in items:
- if "addalbum" in item.keywords:
- item.add_marker(skip_addalbum)
+ if not (config.getoption("--timewarp") and TEST_LIBRARY_TIMEWARP is not None):
+ skip_timewarp = pytest.mark.skip(
+ reason="need --timewarp option and MacOS Catalina to run"
+ )
+ for item in items:
+ if "timewarp" in item.keywords:
+ item.add_marker(skip_timewarp)
-def copy_photos_library(photos_library=TEST_LIBRARY, delay=0):
+def copy_photos_library(photos_library, delay=0):
"""copy the test library and open Photos, returns path to copied library"""
script = AppleScript(
"""
@@ -134,3 +165,33 @@ def delete_crash_logs():
path = pathlib.Path(os.getcwd()) / "osxphotos_crash.log"
if path.is_file():
path.unlink()
+
+
+@pytest.fixture
+def photoslib():
+ return photoscript.PhotosLibrary()
+
+
+@pytest.fixture
+def suspend_capture(pytestconfig):
+ class suspend_guard:
+ def __init__(self):
+ self.capmanager = pytestconfig.pluginmanager.getplugin("capturemanager")
+
+ def __enter__(self):
+ self.capmanager.suspend_global_capture(in_=True)
+
+ def __exit__(self, _1, _2, _3):
+ self.capmanager.resume_global_capture()
+
+ yield suspend_guard()
+
+
+@pytest.fixture
+def output_file():
+ """Create a temporary filename for writing output"""
+ tempdir = tempfile.gettempdir()
+ fd, filename = tempfile.mkstemp(dir=tempdir)
+ os.close(fd)
+ yield filename
+ os.remove(filename)
diff --git a/tests/parse_timewarp_output.py b/tests/parse_timewarp_output.py
new file mode 100644
index 00000000..c391808b
--- /dev/null
+++ b/tests/parse_timewarp_output.py
@@ -0,0 +1,53 @@
+""" Parse --inspect and --compare-exif output for testing"""
+
+from collections import namedtuple
+from typing import List
+
+# filename, uuid, photo time (local), photo time, timezone offset, timezone name
+InspectValues = namedtuple(
+ "InspectValues",
+ ["filename", "uuid", "date_local", "date_tz", "tz_offset", "tz_name"],
+)
+
+CompareValues = namedtuple(
+ "CompareValues",
+ [
+ "filename",
+ "uuid",
+ "date_photos",
+ "date_exif",
+ "tz_offset_photos",
+ "tz_offset_exif",
+ ],
+)
+
+
+def parse_inspect_output(output: str) -> List[InspectValues]:
+ """Parse output of --inspect and return list of InspectValues named tuple"""
+
+ with open(output, "r") as f:
+ lines = f.readlines()
+ lines = [line for line in lines if line.strip()]
+ # remove header
+ lines.pop(0)
+ values = []
+ for line in lines:
+ parts = line.split(",")
+ parts = [part.strip() for part in parts]
+ values.append(InspectValues(*parts))
+ return values
+
+
+def parse_compare_exif(output: str) -> List[CompareValues]:
+ """Parse output of --compare-exif and return list of CompareValues named tuple"""
+ with open(output, "r") as f:
+ lines = f.readlines()
+ lines = [line for line in lines if line.strip()]
+ # remove header
+ lines.pop(0)
+ values = []
+ for line in lines:
+ parts = line.split(",")
+ parts = [part.strip() for part in parts]
+ values.append(CompareValues(*parts))
+ return values
diff --git a/tests/test_cli_timewarp.py b/tests/test_cli_timewarp.py
new file mode 100644
index 00000000..b050ed1e
--- /dev/null
+++ b/tests/test_cli_timewarp.py
@@ -0,0 +1,937 @@
+""" Tests which require user interaction to run for osxphotos timewarp command """
+
+import os
+import time
+
+import pytest
+from click.testing import CliRunner
+from osxphotos import PhotosDB
+from osxphotos.exiftool import ExifTool
+
+from tests.conftest import (
+ get_os_version,
+)
+from tests.parse_timewarp_output import parse_compare_exif, parse_inspect_output
+
+
+# set timezone to avoid issues with comparing dates
+os.environ["TZ"] = "US/Pacific"
+time.tzset()
+
+TERMINAL_WIDTH = 250
+
+OS_VER = get_os_version()[1]
+if OS_VER == "15":
+ from tests.config_timewarp_catalina import CATALINA_PHOTOS_5 as TEST_DATA
+
+else:
+ TEST_DATA = {}
+
+
+def say(msg: str) -> None:
+ """Say message with text to speech"""
+ os.system(f"say {msg}")
+
+
+def ask_user_to_make_selection(
+ photoslib, suspend_capture, photo_name: str, retry=3, video=False
+) -> bool:
+ """Ask user to make selection
+
+ Args:
+ photoslib: photoscript.PhotosLibrary instance passed from fixture
+ suspend_capture: suspend capture fixture
+ photo_name: name of the photo ask user for
+ retry: number of times to retry before failing
+ video: set to True if asking for a video instead of a photo
+ """
+ # needs to be called with a suspend_capture fixture
+ photo_or_video = "photo" if not video else "video"
+ tries = 0
+ while tries < retry:
+ with suspend_capture:
+ prompt = f"Select the {photo_or_video} of the {photo_name} then press Enter in the Terminal."
+ say(prompt)
+ input(f"\n{prompt}")
+
+ selection = photoslib.selection
+ if (
+ len(selection) == 1
+ and selection[0].filename == TEST_DATA["filenames"][photo_name]
+ ):
+ return True
+ tries += 1
+ return False
+
+
+########## Interactive tests run first ##########
+
+
+@pytest.mark.timewarp
+def test_select_pears(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "pears")
+
+
+@pytest.mark.timewarp
+def test_inspect(photoslib, suspend_capture, output_file):
+ """Test --inspect. NOTE: this test requires user interaction"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ values = parse_inspect_output(output_file)
+ assert TEST_DATA["inspect"]["expected"] == values
+
+
+@pytest.mark.timewarp
+def test_date(photoslib, suspend_capture):
+ """Test --date"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--date",
+ TEST_DATA["date"]["value"],
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ photo = photoslib.selection[0]
+ assert photo.date == TEST_DATA["date"]["date"]
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["date_delta"]["parameters"])
+def test_date_delta(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --date-delta"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--date-delta",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["time"]["parameters"])
+def test_time(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --time"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--time",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ # inspect to get the updated times
+ # don't use photo.date as it will return local time instead of the time in the timezone
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["time_delta"]["parameters"])
+def test_time_delta(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --time-delta"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--time-delta",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "input_value,expected_date,expected_tz", TEST_DATA["time_zone"]["parameters"]
+)
+def test_time_zone(
+ photoslib, suspend_capture, input_value, expected_date, expected_tz, output_file
+):
+ """Test --time-zone"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--timezone",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected_date
+ assert output_values[0].tz_offset == expected_tz
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("expected", TEST_DATA["compare_exif"]["expected"])
+def test_compare_exif(photoslib, suspend_capture, expected, output_file):
+ """Test --compare-exif"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--compare-exif",
+ "--plain",
+ "-o",
+ output_file,
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "expected,album", TEST_DATA["compare_exif_add_to_album"]["expected"]
+)
+def test_compare_exif_add_to_album(photoslib, suspend_capture, expected, album):
+ """Test --compare-exif --add-to-album"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--compare-exif",
+ "--add-to-album",
+ album,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ assert expected in result.output
+ photo = photoslib.selection[0]
+ assert album in [album.name for album in photo.albums]
+
+
+@pytest.mark.timewarp
+def test_select_sunflowers(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "sunflowers")
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("expected", TEST_DATA["compare_exif_3"]["expected"])
+def test_compare_exif_3(photoslib, suspend_capture, expected, output_file):
+ """Test --compare-exif"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["match"]["parameters"])
+def test_match(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --timezone --match"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--timezone",
+ input_value,
+ "--match-time",
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+def test_push_exif_missing_file():
+ """Test --push-exif when an original file is missing"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp, ["--push-exif", "--plain", "--verbose"], terminal_width=TERMINAL_WIDTH
+ )
+ assert result.exit_code == 0
+ assert "Skipping EXIF update for missing photo" in result.output
+
+
+@pytest.mark.timewarp
+def test_select_pumpkins(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "pumpkins")
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "match,tz_value,time_delta_value,expected_date,exif_date,exif_offset",
+ TEST_DATA["exiftool"]["parameters"],
+)
+def test_push_exif_1(
+ photoslib,
+ match,
+ tz_value,
+ time_delta_value,
+ expected_date,
+ exif_date,
+ exif_offset,
+ output_file,
+):
+ """Test --timezone --match with --push-exif"""
+ from osxphotos.cli.timewarp import timewarp
+
+ cli_args = [
+ "--timezone",
+ tz_value,
+ "--time-delta",
+ time_delta_value,
+ "--push-exif",
+ "--plain",
+ ]
+ if match:
+ cli_args.append("--match-time")
+
+ runner = CliRunner()
+ result = runner.invoke(timewarp, cli_args, terminal_width=TERMINAL_WIDTH)
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected_date
+
+ photo = photoslib.selection[0]
+ uuid = photo.uuid
+ path = PhotosDB().get_photo(uuid).path
+ exif = ExifTool(path)
+ exifdict = exif.asdict()
+ assert exifdict["EXIF:DateTimeOriginal"] == exif_date
+ assert exifdict["EXIF:OffsetTimeOriginal"] == exif_offset
+
+
+@pytest.mark.timewarp
+def test_select_pears_2(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "pears")
+
+
+@pytest.mark.timewarp
+def test_push_exif_2(photoslib, suspend_capture, output_file):
+ """Test --push-exif"""
+ pre_test = TEST_DATA["push_exif"]["pre"]
+ post_test = TEST_DATA["push_exif"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--push-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_pull_exif_1(photoslib, suspend_capture, output_file):
+ """Test --pull-exif"""
+ pre_test = TEST_DATA["pull_exif_1"]["pre"]
+ post_test = TEST_DATA["pull_exif_1"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ # update the photo so we know if the data is updated
+ result = runner.invoke(
+ timewarp,
+ ["-z", "-0400", "-D", "+1 day", "-m", "-V", "--plain"],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_select_apple_tree(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "apple tree")
+
+
+@pytest.mark.timewarp
+def test_pull_exif_no_time(photoslib, suspend_capture, output_file):
+ """Test --pull-exif when photo has invalid date/time in EXIF"""
+ pre_test = TEST_DATA["pull_exif_no_time"]["pre"]
+ post_test = TEST_DATA["pull_exif_no_time"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_select_marigolds(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "marigold flowers")
+
+
+@pytest.mark.timewarp
+def test_pull_exif_no_offset(photoslib, suspend_capture, output_file):
+ """Test --pull-exif when photo has no offset in EXIF"""
+ pre_test = TEST_DATA["pull_exif_no_offset"]["pre"]
+ post_test = TEST_DATA["pull_exif_no_offset"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_select_zinnias(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(
+ photoslib, suspend_capture, "multi-colored zinnia flowers"
+ )
+
+
+@pytest.mark.timewarp
+def test_pull_exif_no_data(photoslib, suspend_capture, output_file):
+ """Test --pull-exif when photo has no data in EXIF"""
+ pre_test = TEST_DATA["pull_exif_no_data"]["pre"]
+ post_test = TEST_DATA["pull_exif_no_data"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ assert "Skipping update for missing EXIF data in photo" in result.output
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_pull_exif_no_data_use_file_time(photoslib, suspend_capture, output_file):
+ """Test --pull-exif when photo has no data in EXIF with --use-file-time"""
+ pre_test = TEST_DATA["pull_exif_no_data_use_file_time"]["pre"]
+ post_test = TEST_DATA["pull_exif_no_data_use_file_time"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ "--use-file-time",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ assert "EXIF date/time missing, using file modify date/time" in result.output
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_select_sunset_video(photoslib, suspend_capture):
+ """Force user to select the right photo for following tests"""
+ assert ask_user_to_make_selection(photoslib, suspend_capture, "sunset", video=True)
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("expected", TEST_DATA["compare_video_1"]["expected"])
+def test_video_compare_exif(photoslib, suspend_capture, expected, output_file):
+ """Test --compare-exif with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--compare-exif",
+ "--plain",
+ "-o",
+ output_file,
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "input_value,expected", TEST_DATA["video_date_delta"]["parameters"]
+)
+def test_video_date_delta(
+ photoslib, suspend_capture, input_value, expected, output_file
+):
+ """Test --date-delta with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--date-delta",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "input_value,expected", TEST_DATA["video_time_delta"]["parameters"]
+)
+def test_video_time_delta(
+ photoslib, suspend_capture, input_value, expected, output_file
+):
+ """Test --time-delta with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--time-delta",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["video_date"]["parameters"])
+def test_video_date(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --date with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--date",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ # inspect to get the updated times
+ # don't use photo.date as it will return local time instead of the time in the timezone
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["video_time"]["parameters"])
+def test_video_time(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --time with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--time",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ # inspect to get the updated times
+ # don't use photo.date as it will return local time instead of the time in the timezone
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize(
+ "input_value,expected_date,expected_tz", TEST_DATA["video_time_zone"]["parameters"]
+)
+def test_video_time_zone(
+ photoslib, suspend_capture, input_value, expected_date, expected_tz, output_file
+):
+ """Test --time-zone"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--timezone",
+ input_value,
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected_date
+ assert output_values[0].tz_offset == expected_tz
+
+
+@pytest.mark.timewarp
+@pytest.mark.parametrize("input_value,expected", TEST_DATA["video_match"]["parameters"])
+def test_video_match(photoslib, suspend_capture, input_value, expected, output_file):
+ """Test --timezone --match with video"""
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+ result = runner.invoke(
+ timewarp,
+ [
+ "--timezone",
+ input_value,
+ "--match-time",
+ "--plain",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+ result = runner.invoke(
+ timewarp,
+ ["--inspect", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_inspect_output(output_file)
+ assert output_values[0].date_tz == expected
+
+
+@pytest.mark.timewarp
+def test_video_push_exif(photoslib, suspend_capture, output_file):
+ """Test --push-exif with video"""
+ pre_test = TEST_DATA["video_push_exif"]["pre"]
+ post_test = TEST_DATA["video_push_exif"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--push-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
+
+
+@pytest.mark.timewarp
+def test_video_pull_exif(photoslib, suspend_capture, output_file):
+ """Test --pull-exif with video"""
+ pre_test = TEST_DATA["video_pull_exif"]["pre"]
+ post_test = TEST_DATA["video_pull_exif"]["post"]
+
+ from osxphotos.cli.timewarp import timewarp
+
+ runner = CliRunner()
+
+ # update the photo so we know if the data is updated
+ result = runner.invoke(
+ timewarp,
+ ["-z", "-0500", "-D", "+1 day", "-T", "-10 hours", "-m", "-V", "--plain"],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == pre_test
+
+ result = runner.invoke(
+ timewarp,
+ [
+ "--pull-exif",
+ "--plain",
+ "--verbose",
+ ],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ timewarp,
+ ["--compare-exif", "--plain", "-o", output_file],
+ terminal_width=TERMINAL_WIDTH,
+ )
+ output_values = parse_compare_exif(output_file)
+ assert output_values[0] == post_test
|