Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfcb99f377 | ||
|
|
8e9f27995b | ||
|
|
79e4b333e9 | ||
|
|
6d5af5c5e8 | ||
|
|
5a3e32a2b4 | ||
|
|
3473c2ece2 | ||
|
|
4ec9f6d3e6 | ||
|
|
5a9722b37c | ||
|
|
72af96b48e | ||
|
|
ba4d878b1f | ||
|
|
1fb8fa7da2 | ||
|
|
1173b6c0f2 | ||
|
|
470839ba0d | ||
|
|
391815dd94 | ||
|
|
b4dc7cfcf6 | ||
|
|
a89c66b3f7 | ||
|
|
5cd74b5f23 |
@@ -1943,7 +1943,7 @@ cog.out(get_template_field_table())
|
||||
|{lf}|A line feed: '\n', alias for {newline}|
|
||||
|{cr}|A carriage return: '\r'|
|
||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.48.6'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.2'|
|
||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||
|{album}|Album(s) photo is contained in|
|
||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||
@@ -2103,6 +2103,7 @@ Attributes:
|
||||
|
||||
`ExportResults` has the following properties:
|
||||
|
||||
* datetime: date/time of export in ISO 8601 format
|
||||
* exported: list of all exported files (A single call to export could export more than one file, e.g. original file, preview, live video, raw, etc.)
|
||||
* new: list of new files exported when used with update=True
|
||||
* updated: list of updated files when used with update=True
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.49.1](https://github.com/RhetTbull/osxphotos/compare/v0.49.0...v0.49.1)
|
||||
|
||||
> 17 May 2022
|
||||
|
||||
- Implemented #689 [`4ec9f6d`](https://github.com/RhetTbull/osxphotos/commit/4ec9f6d3e606f72962e351432a64a24ff9153d87)
|
||||
- Unhid exportdb command [`5a9722b`](https://github.com/RhetTbull/osxphotos/commit/5a9722b37c8326bde7c6bde9870ce704aabb2a15)
|
||||
- Added example [`72af96b`](https://github.com/RhetTbull/osxphotos/commit/72af96b48ee7552d5d35e6f5d619c5b7a55050ab)
|
||||
|
||||
#### [v0.49.0](https://github.com/RhetTbull/osxphotos/compare/v0.48.8...v0.49.0)
|
||||
|
||||
> 15 May 2022
|
||||
|
||||
- Feature report writer #309 [`#690`](https://github.com/RhetTbull/osxphotos/pull/690)
|
||||
- Added JSON, SQLite report formats, added command [`1173b6c`](https://github.com/RhetTbull/osxphotos/commit/1173b6c0f294e12269d4015d2823b590df6f9696)
|
||||
- --report can now accept a template, #339 [`391815d`](https://github.com/RhetTbull/osxphotos/commit/391815dd9401fb0b47c0f935cf844bbb85e5ae55)
|
||||
- Fixed run command to allow passing args to the called python script [`b4dc7cf`](https://github.com/RhetTbull/osxphotos/commit/b4dc7cfcf6af1a4b88fc826b99a900318f5b1c84)
|
||||
- Fixed test to run on MacOS > Catalina [`a89c66b`](https://github.com/RhetTbull/osxphotos/commit/a89c66b3f773b31199febc3daf1d2441f3fdfa43)
|
||||
|
||||
#### [v0.48.8](https://github.com/RhetTbull/osxphotos/compare/v0.48.7...v0.48.8)
|
||||
|
||||
> 8 May 2022
|
||||
|
||||
- version bump [`5cd74b5`](https://github.com/RhetTbull/osxphotos/commit/5cd74b5f2373b48c261bea3c6378945ee216531a)
|
||||
|
||||
#### [v0.48.7](https://github.com/RhetTbull/osxphotos/compare/v0.48.6...v0.48.7)
|
||||
|
||||
> 8 May 2022
|
||||
|
||||
- Fixed path_derivatives for shared photos, #687 [`abbb200`](https://github.com/RhetTbull/osxphotos/commit/abbb200838535e03cf4adcb339ad3a2c18f32dc3)
|
||||
- Added --no-keyword, #637 [`e7eefce`](https://github.com/RhetTbull/osxphotos/commit/e7eefce5c51e1e17e64ac44b6c0a9944873f68a3)
|
||||
- Fixed typo in docs [`2ed6e11`](https://github.com/RhetTbull/osxphotos/commit/2ed6e1142644e686b6b645d3bedab3648e13f9df)
|
||||
|
||||
#### [v0.48.6](https://github.com/RhetTbull/osxphotos/compare/v0.48.5...v0.48.6)
|
||||
|
||||
> 7 May 2022
|
||||
|
||||
66
README.md
66
README.md
@@ -18,9 +18,10 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
|
||||
* [Supported operating systems](#supported-operating-systems)
|
||||
* [Installation](#installation)
|
||||
* [Command Line Usage](#command-line-usage)
|
||||
* [Command line examples](#command-line-examples)
|
||||
* [Command Line Examples](#command-line-examples)
|
||||
* [Tutorial](#tutorial)
|
||||
* [Command line reference: export](#command-line-reference-export)
|
||||
* [Command Line Reference: export](#command-line-reference-export)
|
||||
* [Files Created By OSXPhotos](#files-created-by-osxphotos)
|
||||
* [Package Interface](#package-interface)
|
||||
* [PhotosDB](#photosdb)
|
||||
* [PhotoInfo](#photoinfo)
|
||||
@@ -130,17 +131,17 @@ Usage: osxphotos [OPTIONS] COMMAND [ARGS]...
|
||||
osxphotos: query and export your Photos library
|
||||
|
||||
Options:
|
||||
--db <Photos database path> Specify Photos database path. Path to Photos
|
||||
library/database can be specified using either
|
||||
--db or directly as PHOTOS_LIBRARY positional
|
||||
argument. If neither --db or PHOTOS_LIBRARY
|
||||
provided, will attempt to find the library to
|
||||
use in the following order: 1. last opened
|
||||
library, 2. system library, 3.
|
||||
~/Pictures/Photos Library.photoslibrary
|
||||
--json Print output in JSON format.
|
||||
-v, --version Show the version and exit.
|
||||
-h, --help Show this message and exit.
|
||||
--db PHOTOS_LIBRARY_PATH Specify Photos database path. Path to Photos
|
||||
library/database can be specified using either
|
||||
--db or directly as PHOTOS_LIBRARY positional
|
||||
argument. If neither --db or PHOTOS_LIBRARY
|
||||
provided, will attempt to find the library to use
|
||||
in the following order: 1. last opened library, 2.
|
||||
system library, 3. ~/Pictures/Photos
|
||||
Library.photoslibrary
|
||||
--json Print output in JSON format.
|
||||
-v, --version Show the version and exit.
|
||||
-h, --help Show this message and exit.
|
||||
|
||||
Commands:
|
||||
about Print information about osxphotos including license.
|
||||
@@ -148,7 +149,9 @@ Commands:
|
||||
diff Compare two Photos databases and print out differences
|
||||
docs Open osxphotos documentation in your browser.
|
||||
dump Print list of all photos & associated info from the Photos...
|
||||
exiftool Run exiftool on previously exported files to update metadata.
|
||||
export Export photos from the Photos database.
|
||||
exportdb Utilities for working with the osxphotos export database
|
||||
help Print help; for help on commands: help <command>.
|
||||
info Print out descriptive info of the Photos library database.
|
||||
install Install Python packages into the same environment as osxphotos
|
||||
@@ -159,7 +162,7 @@ Commands:
|
||||
places Print out places found in the Photos library.
|
||||
query Query the Photos database using 1 or more search options; if...
|
||||
repl Run interactive osxphotos REPL shell (useful for debugging,...
|
||||
run Run a python file using same environment as osxphotos
|
||||
run Run a python file using same environment as osxphotos.
|
||||
snap Create snapshot of Photos database to use with diff command
|
||||
theme Manage osxphotos color themes.
|
||||
timewarp Adjust date/time/timezone of photos in Apple Photos.
|
||||
@@ -378,7 +381,7 @@ Photos tracks a tremendous amount of metadata associated with photos in the libr
|
||||
|
||||
`osxphotos export /path/to/export --exiftool`
|
||||
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with `--exiftool` to modify the metadata that is written by `exiftool`. For example, you can use the `--keyword-template` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchal keywords in the format used by Lightroom Classic:
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with `--exiftool` to modify the metadata that is written by `exiftool`. For example, you can use the `--keyword-template` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:
|
||||
|
||||
osxphotos export /path/to/export --exiftool --keyword-template "{folder_album(>)}"
|
||||
│ │
|
||||
@@ -390,7 +393,7 @@ This will write basic metadata such as keywords, persons, and GPS location to th
|
||||
for joining the folders and albums. For example,
|
||||
if photo is in Folder1/Folder2/Album, (>) produces
|
||||
"Folder1>Folder2>Album" which some programs, such as
|
||||
Lightroom Classic, treat as hierarchal keywords
|
||||
Lightroom Classic, treat as hierarchical keywords
|
||||
|
||||
The above command will write all the regular metadata that `--exiftool` normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form "Folder1>Folder2>Album". If you did not include the `(>)` in the template string (e.g. `{folder_album}`), folder_album would render in form "Folder1/Folder2/Album".
|
||||
|
||||
@@ -649,6 +652,7 @@ Options:
|
||||
--keyword KEYWORD Search for photos with keyword KEYWORD. If
|
||||
more than one keyword, treated as "OR", e.g.
|
||||
find photos matching any keyword
|
||||
--no-keyword Search for photos with no keyword.
|
||||
--person PERSON Search for photos with person PERSON. If more
|
||||
than one person, treated as "OR", e.g. find
|
||||
photos matching any person
|
||||
@@ -1166,8 +1170,19 @@ Options:
|
||||
iTerm2 (use with Terminal.app). This is faster
|
||||
and more reliable than the default AppleScript
|
||||
interface.
|
||||
--report REPORT_FILE Write a CSV formatted report of all files that
|
||||
were exported.
|
||||
--report REPORT_FILE Write a report of all files that were
|
||||
exported. The extension of the report filename
|
||||
will be used to determine the format. Valid
|
||||
extensions are: .csv (CSV file), .json (JSON),
|
||||
.db and .sqlite (SQLite database). REPORT_FILE
|
||||
may be a template string (see Templating
|
||||
System), for example, --report
|
||||
'export_{today.date}.csv' will write a CSV
|
||||
report file named with today's date. See also
|
||||
--append.
|
||||
--append If used with --report, add data to existing
|
||||
report file instead of overwriting it. See
|
||||
also --report.
|
||||
--cleanup Cleanup export directory by deleting any files
|
||||
which were not included in this export set.
|
||||
For example, photos which had previously been
|
||||
@@ -1834,7 +1849,7 @@ Substitution Description
|
||||
{lf} A line feed: '\n', alias for {newline}
|
||||
{cr} A carriage return: '\r'
|
||||
{crlf} a carriage return + line feed: '\r\n'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.48.6'
|
||||
{osxphotos_version} The osxphotos version, e.g. '0.49.2'
|
||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||
|
||||
The following substitutions may result in multiple values. Thus if specified
|
||||
@@ -2040,6 +2055,16 @@ not be called if the --dry-run flag is set.
|
||||
```
|
||||
<!-- OSXPHOTOS-EXPORT-USAGE:END -->
|
||||
|
||||
### Files Created By OSXPhotos
|
||||
|
||||
The OSXPhotos command line tool creates a number of files during the course of its execution.
|
||||
OSXPhotos adheres to the [XDG](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) standard for file locations.
|
||||
|
||||
* `$XDG_CONFIG_HOME` or `$HOME/.config`: `osxphotos` directory containing configuration files, for example color themes for colorized output.
|
||||
* `$XDG_DATA_HOME` or `$HOME/.local/share`: `osxphotos` directory containing local data files, for example, the help files displayed with `osxphotos docs`.
|
||||
* Current working dir: `osxphotos_crash.log` file containing the stack trace of the last crash if OSXPhotos encounters a fatal error during execution.
|
||||
* export directory (when running `osxphotos export` command): `.osxphotos_export.db` [SQLite](https://www.sqlite.org/index.html) database containing information needed to update an export and track metadata changes in exported photos. *Note*: This file may contain sensitive information such as locations and the names of persons in photos so if you are using `osxphotos export` to share with others, you may want to delete this file. You can also specify an alternate location for the export database using the `--exportdb` flag during export. See also `osxphotos help exportdb` for more information about built in utilities for working with the export database.
|
||||
|
||||
## Example uses of the package
|
||||
|
||||
```python
|
||||
@@ -4000,7 +4025,7 @@ The following template field substitutions are availabe for use the templating s
|
||||
|{lf}|A line feed: '\n', alias for {newline}|
|
||||
|{cr}|A carriage return: '\r'|
|
||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.48.6'|
|
||||
|{osxphotos_version}|The osxphotos version, e.g. '0.49.2'|
|
||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||
|{album}|Album(s) photo is contained in|
|
||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||
@@ -4160,6 +4185,7 @@ Attributes:
|
||||
|
||||
`ExportResults` has the following properties:
|
||||
|
||||
* datetime: date/time of export in ISO 8601 format
|
||||
* exported: list of all exported files (A single call to export could export more than one file, e.g. original file, preview, live video, raw, etc.)
|
||||
* new: list of new files exported when used with update=True
|
||||
* updated: list of updated files when used with update=True
|
||||
|
||||
@@ -102,7 +102,9 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
|
||||
diff Compare two Photos databases and print out differences
|
||||
docs Open osxphotos documentation in your browser.
|
||||
dump Print list of all photos & associated info from the Photos...
|
||||
exiftool Run exiftool on previously exported files to update metadata.
|
||||
export Export photos from the Photos database.
|
||||
exportdb Utilities for working with the osxphotos export database
|
||||
help Print help; for help on commands: help <command>.
|
||||
info Print out descriptive info of the Photos library database.
|
||||
install Install Python packages into the same environment as osxphotos
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 7c9b3fd4a18d08287a7ba12c4e147bf5
|
||||
config: 3a7efae54b6e07cdaf59f7d2e9729b6e
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>Overview: module code - osxphotos 0.48.6 documentation</title>
|
||||
<title>Overview: module code - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -1,43 +1,206 @@
|
||||
<!doctype html>
|
||||
<html class="no-js">
|
||||
<head><meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<!DOCTYPE html>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.exiftool - osxphotos 0.49.0 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||
|
||||
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>osxphotos.exiftool — osxphotos 0.47.9 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/alabaster.css" />
|
||||
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<link rel="stylesheet" href="../../_static/custom.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
--color-code-background: #f8f8f8;
|
||||
--color-code-foreground: black;
|
||||
|
||||
}
|
||||
@media not print {
|
||||
body[data-theme="dark"] {
|
||||
--color-code-background: #202020;
|
||||
--color-code-foreground: #d0d0d0;
|
||||
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body:not([data-theme="light"]) {
|
||||
--color-code-background: #202020;
|
||||
--color-code-foreground: #d0d0d0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style></head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
|
||||
</script>
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="svg-toc" viewBox="0 0 24 24">
|
||||
<title>Contents</title>
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
|
||||
</svg>
|
||||
</symbol>
|
||||
<symbol id="svg-menu" viewBox="0 0 24 24">
|
||||
<title>Menu</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</symbol>
|
||||
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
|
||||
<title>Expand</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
|
||||
<polyline points="9 18 15 12 9 6"></polyline>
|
||||
</svg>
|
||||
</symbol>
|
||||
<symbol id="svg-sun" viewBox="0 0 24 24">
|
||||
<title>Light mode</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
</symbol>
|
||||
<symbol id="svg-moon" viewBox="0 0 24 24">
|
||||
<title>Dark mode</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
||||
</svg>
|
||||
</symbol>
|
||||
<symbol id="svg-sun-half" viewBox="0 0 24 24">
|
||||
<title>Auto light/dark mode</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M13 12h5" />
|
||||
<path d="M13 15h4" />
|
||||
<path d="M13 18h1" />
|
||||
<path d="M13 9h4" />
|
||||
<path d="M13 6h1" />
|
||||
</svg>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
|
||||
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
|
||||
<label class="overlay sidebar-overlay" for="__navigation">
|
||||
<div class="visually-hidden">Hide navigation sidebar</div>
|
||||
</label>
|
||||
<label class="overlay toc-overlay" for="__toc">
|
||||
<div class="visually-hidden">Hide table of contents sidebar</div>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<div class="page">
|
||||
<header class="mobile-header">
|
||||
<div class="header-left">
|
||||
<label class="nav-overlay-icon" for="__navigation">
|
||||
<div class="visually-hidden">Toggle site navigation sidebar</div>
|
||||
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.0 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
<button class="theme-toggle">
|
||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
|
||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
||||
</label>
|
||||
</div>
|
||||
</header>
|
||||
<aside class="sidebar-drawer">
|
||||
<div class="sidebar-container">
|
||||
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head><body>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.0 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
<input type="hidden" name="check_keywords" value="yes">
|
||||
<input type="hidden" name="area" value="default">
|
||||
</form>
|
||||
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">OSXPhotos</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">OSXPhotos Tutorial</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">OSXPhotos Command Line Interface (CLI)</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../template_help.html">OSXPhotos Template System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../package_overview.html">OSXPhotos Python Package Overview</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">OSXPhotos python API</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body" role="main">
|
||||
|
||||
<h1>Source code for osxphotos.exiftool</h1><div class="highlight"><pre>
|
||||
<span></span><span class="sd">""" Yet another simple exiftool wrapper </span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<div class="article-container">
|
||||
<a href="#" class="back-to-top muted-link">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
|
||||
</svg>
|
||||
<span>Back to top</span>
|
||||
</a>
|
||||
<div class="content-icon-container"><div class="theme-toggle-container theme-toggle-content">
|
||||
<button class="theme-toggle">
|
||||
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
|
||||
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||||
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||||
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
|
||||
<div class="visually-hidden">Toggle table of contents sidebar</div>
|
||||
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
|
||||
</label>
|
||||
</div>
|
||||
<article role="main">
|
||||
<h1>Source code for osxphotos.exiftool</h1><div class="highlight"><pre>
|
||||
<span></span><span class="sd">""" Yet another simple exiftool wrapper </span>
|
||||
<span class="sd"> I rolled my own for following reasons: </span>
|
||||
<span class="sd"> 1. I wanted something under MIT license (best alternative was licensed under GPL/BSD)</span>
|
||||
<span class="sd"> 2. I wanted singleton behavior so only a single exiftool process was ever running</span>
|
||||
<span class="sd"> 3. When used as a context manager, I wanted the operations to batch until exiting the context (improved performance)</span>
|
||||
<span class="sd"> If these aren't important to you, I highly recommend you use Sven Marnach's excellent </span>
|
||||
<span class="sd"> pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """</span>
|
||||
<span class="sd"> If these aren't important to you, I highly recommend you use Sven Marnach's excellent </span>
|
||||
<span class="sd"> pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">atexit</span>
|
||||
<span class="kn">import</span> <span class="nn">html</span>
|
||||
@@ -52,103 +215,102 @@
|
||||
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span> <span class="c1"># pylint: disable=syntax-error</span>
|
||||
|
||||
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="s2">"escape_str"</span><span class="p">,</span>
|
||||
<span class="s2">"exiftool_can_write"</span><span class="p">,</span>
|
||||
<span class="s2">"ExifTool"</span><span class="p">,</span>
|
||||
<span class="s2">"ExifToolCaching"</span><span class="p">,</span>
|
||||
<span class="s2">"get_exiftool_path"</span><span class="p">,</span>
|
||||
<span class="s2">"terminate_exiftool"</span><span class="p">,</span>
|
||||
<span class="s2">"unescape_str"</span><span class="p">,</span>
|
||||
<span class="s2">"escape_str"</span><span class="p">,</span>
|
||||
<span class="s2">"exiftool_can_write"</span><span class="p">,</span>
|
||||
<span class="s2">"ExifTool"</span><span class="p">,</span>
|
||||
<span class="s2">"ExifToolCaching"</span><span class="p">,</span>
|
||||
<span class="s2">"get_exiftool_path"</span><span class="p">,</span>
|
||||
<span class="s2">"terminate_exiftool"</span><span class="p">,</span>
|
||||
<span class="s2">"unescape_str"</span><span class="p">,</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="c1"># exiftool -stay_open commands outputs this EOF marker after command is run</span>
|
||||
<span class="n">EXIFTOOL_STAYOPEN_EOF</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{ready}</span><span class="s2">"</span>
|
||||
<span class="n">EXIFTOOL_STAYOPEN_EOF</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{ready}</span><span class="s2">"</span>
|
||||
<span class="n">EXIFTOOL_STAYOPEN_EOF_LEN</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">EXIFTOOL_STAYOPEN_EOF</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># list of exiftool processes to cleanup when exiting or when terminate is called</span>
|
||||
<span class="n">EXIFTOOL_PROCESSES</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
|
||||
<span class="c1"># exiftool supported file types, created by utils/exiftool_supported_types.py</span>
|
||||
<span class="n">EXIFTOOL_FILETYPES_JSON</span> <span class="o">=</span> <span class="s2">"exiftool_filetypes.json"</span>
|
||||
<span class="k">with</span> <span class="p">(</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">EXIFTOOL_FILETYPES_JSON</span><span class="p">)</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
|
||||
<span class="n">EXIFTOOL_FILETYPES_JSON</span> <span class="o">=</span> <span class="s2">"exiftool_filetypes.json"</span>
|
||||
<span class="k">with</span> <span class="p">(</span><span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">EXIFTOOL_FILETYPES_JSON</span><span class="p">)</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
|
||||
<span class="n">EXIFTOOL_SUPPORTED_FILETYPES</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">exiftool_can_write</span><span class="p">(</span><span class="n">suffix</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||
<span class="sd">"""Return True if exiftool supports writing to a file with the given suffix, otherwise False"""</span>
|
||||
<span class="sd">"""Return True if exiftool supports writing to a file with the given suffix, otherwise False"""</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">suffix</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
<span class="n">suffix</span> <span class="o">=</span> <span class="n">suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">suffix</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"."</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">suffix</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"."</span><span class="p">:</span>
|
||||
<span class="n">suffix</span> <span class="o">=</span> <span class="n">suffix</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
|
||||
<span class="k">return</span> <span class="p">(</span>
|
||||
<span class="n">suffix</span> <span class="ow">in</span> <span class="n">EXIFTOOL_SUPPORTED_FILETYPES</span>
|
||||
<span class="ow">and</span> <span class="n">EXIFTOOL_SUPPORTED_FILETYPES</span><span class="p">[</span><span class="n">suffix</span><span class="p">][</span><span class="s2">"write"</span><span class="p">]</span>
|
||||
<span class="ow">and</span> <span class="n">EXIFTOOL_SUPPORTED_FILETYPES</span><span class="p">[</span><span class="n">suffix</span><span class="p">][</span><span class="s2">"write"</span><span class="p">]</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">escape_str</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
|
||||
<span class="sd">"""escape string for use with exiftool -E"""</span>
|
||||
<span class="sd">"""escape string for use with exiftool -E"""</span>
|
||||
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">s</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">html</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#xa;"</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\t</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#x9;"</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\r</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#xd;"</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#xa;"</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\t</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#x9;"</span><span class="p">)</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\r</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"&#xd;"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">s</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">unescape_str</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
|
||||
<span class="sd">"""unescape an HTML string returned by exiftool -E"""</span>
|
||||
<span class="sd">"""unescape an HTML string returned by exiftool -E"""</span>
|
||||
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">str</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">s</span>
|
||||
<span class="c1"># avoid " in values which result in json.loads() throwing an exception, #636</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"&quot;"</span><span class="p">,</span> <span class="s1">'</span><span class="se">\\</span><span class="s1">"'</span><span class="p">)</span>
|
||||
<span class="c1"># avoid " in values which result in json.loads() throwing an exception, #636</span>
|
||||
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"&quot;"</span><span class="p">,</span> <span class="s1">'</span><span class="se">\\</span><span class="s1">"'</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">html</span><span class="o">.</span><span class="n">unescape</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="nd">@atexit</span><span class="o">.</span><span class="n">register</span>
|
||||
<span class="k">def</span> <span class="nf">terminate_exiftool</span><span class="p">():</span>
|
||||
<span class="sd">"""Terminate any running ExifTool subprocesses; call this to cleanup when done using ExifTool"""</span>
|
||||
<span class="sd">"""Terminate any running ExifTool subprocesses; call this to cleanup when done using ExifTool"""</span>
|
||||
<span class="k">for</span> <span class="n">proc</span> <span class="ow">in</span> <span class="n">EXIFTOOL_PROCESSES</span><span class="p">:</span>
|
||||
<span class="n">proc</span><span class="o">.</span><span class="n">_stop_proc</span><span class="p">()</span>
|
||||
|
||||
|
||||
<span class="nd">@lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="k">def</span> <span class="nf">get_exiftool_path</span><span class="p">():</span>
|
||||
<span class="sd">"""return path of exiftool, cache result"""</span>
|
||||
<span class="n">exiftool_path</span> <span class="o">=</span> <span class="n">shutil</span><span class="o">.</span><span class="n">which</span><span class="p">(</span><span class="s2">"exiftool"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">exiftool_path</span><span class="p">:</span>
|
||||
<span class="sd">"""return path of exiftool, cache result"""</span>
|
||||
<span class="k">if</span> <span class="n">exiftool_path</span> <span class="o">:=</span> <span class="n">shutil</span><span class="o">.</span><span class="n">which</span><span class="p">(</span><span class="s2">"exiftool"</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">exiftool_path</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">FileNotFoundError</span><span class="p">(</span>
|
||||
<span class="s2">"Could not find exiftool. Please download and install from "</span>
|
||||
<span class="s2">"https://exiftool.org/"</span>
|
||||
<span class="s2">"Could not find exiftool. Please download and install from "</span>
|
||||
<span class="s2">"https://exiftool.org/"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">_ExifToolProc</span><span class="p">:</span>
|
||||
<span class="sd">"""Runs exiftool in a subprocess via Popen</span>
|
||||
<span class="sd"> Creates a singleton object"""</span>
|
||||
<span class="sd">"""Runs exiftool in a subprocess via Popen</span>
|
||||
<span class="sd"> Creates a singleton object"""</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="sd">"""create new object or return instance of already created singleton"""</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">"instance"</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span><span class="p">:</span>
|
||||
<span class="sd">"""create new object or return instance of already created singleton"""</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s2">"instance"</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span><span class="p">:</span>
|
||||
<span class="bp">cls</span><span class="o">.</span><span class="n">instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
||||
|
||||
<span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">instance</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""construct _ExifToolProc singleton object or return instance of already created object</span>
|
||||
<span class="sd">"""construct _ExifToolProc singleton object or return instance of already created object</span>
|
||||
<span class="sd"> exiftool: optional path to exiftool binary (if not provided, will search path to find it)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"_process_running"</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"_process_running"</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
|
||||
<span class="c1"># already running</span>
|
||||
<span class="k">if</span> <span class="n">exiftool</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">exiftool</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"exiftool subprocess already running, "</span>
|
||||
<span class="sa">f</span><span class="s2">"ignoring exiftool=</span><span class="si">{</span><span class="n">exiftool</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="sa">f</span><span class="s2">"exiftool subprocess already running, "</span>
|
||||
<span class="sa">f</span><span class="s2">"ignoring exiftool=</span><span class="si">{</span><span class="n">exiftool</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
@@ -157,7 +319,7 @@
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return the exiftool subprocess"""</span>
|
||||
<span class="sd">"""return the exiftool subprocess"""</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
@@ -166,37 +328,37 @@
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">pid</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return process id (PID) of the exiftool process"""</span>
|
||||
<span class="sd">"""return process id (PID) of the exiftool process"""</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">pid</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return path to exiftool process"""</span>
|
||||
<span class="sd">"""return path to exiftool process"""</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_start_proc</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""start exiftool in batch mode"""</span>
|
||||
<span class="sd">"""start exiftool in batch mode"""</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"exiftool already running: </span><span class="si">{self._process}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s2">"exiftool already running: </span><span class="si">{self._process}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="c1"># open exiftool process</span>
|
||||
<span class="c1"># make sure /usr/bin at start of path so exiftool can find xattr (see #636)</span>
|
||||
<span class="n">env</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||||
<span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'/usr/bin/:</span><span class="si">{</span><span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span><span class="si">}</span><span class="s1">'</span>
|
||||
<span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'/usr/bin/:</span><span class="si">{</span><span class="n">env</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span><span class="si">}</span><span class="s1">'</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span>
|
||||
<span class="p">[</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_exiftool</span><span class="p">,</span>
|
||||
<span class="s2">"-stay_open"</span><span class="p">,</span> <span class="c1"># keep process open in batch mode</span>
|
||||
<span class="s2">"True"</span><span class="p">,</span> <span class="c1"># -stay_open=True, keep process open in batch mode</span>
|
||||
<span class="s2">"-@"</span><span class="p">,</span> <span class="c1"># read command-line arguments from file</span>
|
||||
<span class="s2">"-"</span><span class="p">,</span> <span class="c1"># read from stdin</span>
|
||||
<span class="s2">"-common_args"</span><span class="p">,</span> <span class="c1"># specifies args common to all commands subsequently run</span>
|
||||
<span class="s2">"-n"</span><span class="p">,</span> <span class="c1"># no print conversion (e.g. print tag values in machine readable format)</span>
|
||||
<span class="s2">"-P"</span><span class="p">,</span> <span class="c1"># Preserve file modification date/time</span>
|
||||
<span class="s2">"-G"</span><span class="p">,</span> <span class="c1"># print group name for each tag</span>
|
||||
<span class="s2">"-E"</span><span class="p">,</span> <span class="c1"># escape tag values for HTML (allows use of HTML &#xa; for newlines)</span>
|
||||
<span class="s2">"-stay_open"</span><span class="p">,</span> <span class="c1"># keep process open in batch mode</span>
|
||||
<span class="s2">"True"</span><span class="p">,</span> <span class="c1"># -stay_open=True, keep process open in batch mode</span>
|
||||
<span class="s2">"-@"</span><span class="p">,</span> <span class="c1"># read command-line arguments from file</span>
|
||||
<span class="s2">"-"</span><span class="p">,</span> <span class="c1"># read from stdin</span>
|
||||
<span class="s2">"-common_args"</span><span class="p">,</span> <span class="c1"># specifies args common to all commands subsequently run</span>
|
||||
<span class="s2">"-n"</span><span class="p">,</span> <span class="c1"># no print conversion (e.g. print tag values in machine readable format)</span>
|
||||
<span class="s2">"-P"</span><span class="p">,</span> <span class="c1"># Preserve file modification date/time</span>
|
||||
<span class="s2">"-G"</span><span class="p">,</span> <span class="c1"># print group name for each tag</span>
|
||||
<span class="s2">"-E"</span><span class="p">,</span> <span class="c1"># escape tag values for HTML (allows use of HTML &#xa; for newlines)</span>
|
||||
<span class="p">],</span>
|
||||
<span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span>
|
||||
<span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span>
|
||||
@@ -208,14 +370,14 @@
|
||||
<span class="n">EXIFTOOL_PROCESSES</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_stop_proc</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""stop the exiftool process if it's running, otherwise, do nothing"""</span>
|
||||
<span class="sd">"""stop the exiftool process if it's running, otherwise, do nothing"""</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process_running</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"-stay_open</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"False</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"-stay_open</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">"False</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
|
||||
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="k">pass</span>
|
||||
@@ -231,10 +393,10 @@
|
||||
|
||||
|
||||
<div class="viewcode-block" id="ExifTool"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool">[docs]</a><span class="k">class</span> <span class="nc">ExifTool</span><span class="p">:</span>
|
||||
<span class="sd">"""Basic exiftool interface for reading and writing EXIF tags"""</span>
|
||||
<span class="sd">"""Basic exiftool interface for reading and writing EXIF tags"""</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">overwrite</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">flags</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""Create ExifTool object</span>
|
||||
<span class="sd">"""Create ExifTool object</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> file: path to image file</span>
|
||||
@@ -244,7 +406,7 @@
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> ExifTool instance</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">file</span> <span class="o">=</span> <span class="n">filepath</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">overwrite</span> <span class="o">=</span> <span class="n">overwrite</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">flags</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
@@ -261,7 +423,7 @@
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span><span class="o">.</span><span class="n">process</span>
|
||||
|
||||
<div class="viewcode-block" id="ExifTool.setvalue"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool.setvalue">[docs]</a> <span class="k">def</span> <span class="nf">setvalue</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||||
<span class="sd">"""Set tag to value(s); if value is None, will delete tag</span>
|
||||
<span class="sd">"""Set tag to value(s); if value is None, will delete tag</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> tag: str; name of tag to set</span>
|
||||
@@ -273,27 +435,27 @@
|
||||
<span class="sd"> If error generated by exiftool, returns False and sets self.error to error string</span>
|
||||
<span class="sd"> If warning generated by exiftool, returns True (unless there was also an error) and sets self.warning to warning string</span>
|
||||
<span class="sd"> If called in context manager, returns True (execution is delayed until exiting context manager)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="s2">""</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="s2">""</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">escape_str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">tag</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2">"</span><span class="p">]</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">tag</span><span class="si">}</span><span class="s2">=</span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2">"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">overwrite</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span><span class="p">:</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># avoid "Warning: Some character(s) could not be encoded in Latin" warning</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-iptc:codedcharacterset=utf8"</span><span class="p">)</span>
|
||||
<span class="c1"># avoid "Warning: Some character(s) could not be encoded in Latin" warning</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-iptc:codedcharacterset=utf8"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_commands</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">error</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="o">*</span><span class="n">command</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">error</span> <span class="o">==</span> <span class="s2">""</span></div>
|
||||
<span class="k">return</span> <span class="n">error</span> <span class="o">==</span> <span class="s2">""</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExifTool.addvalues"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool.addvalues">[docs]</a> <span class="k">def</span> <span class="nf">addvalues</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="o">*</span><span class="n">values</span><span class="p">):</span>
|
||||
<span class="sd">"""Add one or more value(s) to tag</span>
|
||||
<span class="sd">"""Add one or more value(s) to tag</span>
|
||||
<span class="sd"> If more than one value is passed, each value will be added to the tag</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
@@ -311,32 +473,32 @@
|
||||
<span class="sd"> the values being added are not already in the EXIF data</span>
|
||||
<span class="sd"> For some tags, such as IPTC:Keywords, this will add a new value to the list of keywords,</span>
|
||||
<span class="sd"> but for others, such as EXIF:ISO, this will literally add a value to the existing value.</span>
|
||||
<span class="sd"> It's up to the caller to know what exiftool will do for each tag</span>
|
||||
<span class="sd"> It's up to the caller to know what exiftool will do for each tag</span>
|
||||
<span class="sd"> If setvalue called before addvalues, exiftool does not appear to add duplicates,</span>
|
||||
<span class="sd"> but if addvalues called without first calling setvalue, exiftool will add duplicate values</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">values</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Must pass at least one value"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Must pass at least one value"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Can't add None value to tag"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Can't add None value to tag"</span><span class="p">)</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">escape_str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">tag</span><span class="si">}</span><span class="s2">+=</span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">tag</span><span class="si">}</span><span class="s2">+=</span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">overwrite</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span><span class="p">:</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
<span class="n">command</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_commands</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">error</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="o">*</span><span class="n">command</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">error</span> <span class="o">==</span> <span class="s2">""</span></div>
|
||||
<span class="k">return</span> <span class="n">error</span> <span class="o">==</span> <span class="s2">""</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExifTool.run_commands"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool.run_commands">[docs]</a> <span class="k">def</span> <span class="nf">run_commands</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">commands</span><span class="p">,</span> <span class="n">no_file</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="sd">"""Run commands in the exiftool process and return result.</span>
|
||||
<span class="sd">"""Run commands in the exiftool process and return result.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> *commands: exiftool commands to run</span>
|
||||
@@ -350,35 +512,35 @@
|
||||
<span class="sd"> error: if exiftool generated errors, string containing otherwise empty string</span>
|
||||
|
||||
<span class="sd"> Note: Also sets self.warning and self.error if warning or error generated.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"_process"</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="p">):</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"exiftool process is not running"</span><span class="p">)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"_process"</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="p">):</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"exiftool process is not running"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">commands</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"must provide one or more command to run"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"must provide one or more command to run"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">overwrite</span><span class="p">:</span>
|
||||
<span class="n">commands</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">commands</span><span class="p">)</span>
|
||||
<span class="n">commands</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
<span class="n">commands</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"-overwrite_original"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">filename</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fsencode</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">no_file</span> <span class="k">else</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">filename</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">fsencode</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">no_file</span> <span class="k">else</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">flags</span><span class="p">:</span>
|
||||
<span class="c1"># need to split flags, e.g. so "--ext AVI" becomes ["--ext", "AVI"]</span>
|
||||
<span class="c1"># need to split flags, e.g. so "--ext AVI" becomes ["--ext", "AVI"]</span>
|
||||
<span class="n">flags</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">flags</span><span class="p">:</span>
|
||||
<span class="n">flags</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
|
||||
<span class="n">command_str</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">f</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">flags</span><span class="p">])</span>
|
||||
<span class="n">command_str</span> <span class="o">+=</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="n">command_str</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">f</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">flags</span><span class="p">])</span>
|
||||
<span class="n">command_str</span> <span class="o">+=</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">command_str</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">command_str</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
|
||||
<span class="n">command_str</span> <span class="o">+=</span> <span class="p">(</span>
|
||||
<span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">c</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">commands</span><span class="p">])</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">c</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">commands</span><span class="p">])</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="n">filename</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"-execute</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">b</span><span class="s2">"-execute</span><span class="se">\n</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># send the command</span>
|
||||
@@ -386,19 +548,19 @@
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># read the output</span>
|
||||
<span class="n">output</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">warning</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">error</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">output</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">warning</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="n">error</span> <span class="o">=</span> <span class="sa">b</span><span class="s2">""</span>
|
||||
<span class="k">while</span> <span class="n">EXIFTOOL_STAYOPEN_EOF</span> <span class="ow">not</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">output</span><span class="p">):</span>
|
||||
<span class="n">line</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="sa">b</span><span class="s2">"Warning"</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="sa">b</span><span class="s2">"Warning"</span><span class="p">):</span>
|
||||
<span class="n">warning</span> <span class="o">+=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="k">elif</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="sa">b</span><span class="s2">"Error"</span><span class="p">):</span>
|
||||
<span class="k">elif</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="sa">b</span><span class="s2">"Error"</span><span class="p">):</span>
|
||||
<span class="n">error</span> <span class="o">+=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">output</span> <span class="o">+=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="n">warning</span> <span class="o">=</span> <span class="s2">""</span> <span class="k">if</span> <span class="n">warning</span> <span class="o">==</span> <span class="sa">b</span><span class="s2">""</span> <span class="k">else</span> <span class="n">warning</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
<span class="n">error</span> <span class="o">=</span> <span class="s2">""</span> <span class="k">if</span> <span class="n">error</span> <span class="o">==</span> <span class="sa">b</span><span class="s2">""</span> <span class="k">else</span> <span class="n">error</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
<span class="n">warning</span> <span class="o">=</span> <span class="s2">""</span> <span class="k">if</span> <span class="n">warning</span> <span class="o">==</span> <span class="sa">b</span><span class="s2">""</span> <span class="k">else</span> <span class="n">warning</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
<span class="n">error</span> <span class="o">=</span> <span class="s2">""</span> <span class="k">if</span> <span class="n">error</span> <span class="o">==</span> <span class="sa">b</span><span class="s2">""</span> <span class="k">else</span> <span class="n">error</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">warning</span> <span class="o">=</span> <span class="n">warning</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span>
|
||||
|
||||
@@ -406,41 +568,41 @@
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">pid</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return process id (PID) of the exiftool process"""</span>
|
||||
<span class="sd">"""return process id (PID) of the exiftool process"""</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_process</span><span class="o">.</span><span class="n">pid</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">version</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""returns exiftool version"""</span>
|
||||
<span class="n">ver</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-ver"</span><span class="p">,</span> <span class="n">no_file</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">ver</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
<span class="sd">"""returns exiftool version"""</span>
|
||||
<span class="n">ver</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-ver"</span><span class="p">,</span> <span class="n">no_file</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">ver</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
|
||||
|
||||
<div class="viewcode-block" id="ExifTool.asdict"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool.asdict">[docs]</a> <span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag_groups</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">normalized</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="sd">"""return dictionary of all EXIF tags and values from exiftool</span>
|
||||
<span class="sd">"""return dictionary of all EXIF tags and values from exiftool</span>
|
||||
<span class="sd"> returns empty dict if no tags</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> tag_groups: if True (default), dict keys have tag groups, e.g. "IPTC:Keywords"; if False, drops groups from keys, e.g. "Keywords"</span>
|
||||
<span class="sd"> tag_groups: if True (default), dict keys have tag groups, e.g. "IPTC:Keywords"; if False, drops groups from keys, e.g. "Keywords"</span>
|
||||
<span class="sd"> normalized: if True, dict keys are all normalized to lower case (default is False)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">json_str</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-json"</span><span class="p">)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">json_str</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-json"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">json_str</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="nb">dict</span><span class="p">()</span>
|
||||
<span class="n">json_str</span> <span class="o">=</span> <span class="n">unescape_str</span><span class="p">(</span><span class="n">json_str</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
|
||||
<span class="n">json_str</span> <span class="o">=</span> <span class="n">unescape_str</span><span class="p">(</span><span class="n">json_str</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
|
||||
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">json_str</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="c1"># will fail with some commands, e.g --ext AVI which produces</span>
|
||||
<span class="c1"># 'No file with specified extension' instead of json</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">"error loading json returned by exiftool: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">json_str</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="c1"># 'No file with specified extension' instead of json</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">"error loading json returned by exiftool: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">json_str</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="nb">dict</span><span class="p">()</span>
|
||||
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exifdict</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">tag_groups</span><span class="p">:</span>
|
||||
<span class="c1"># strip tag groups</span>
|
||||
<span class="n">exif_new</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">exifdict</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||||
<span class="n">k</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s2">".*:"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
|
||||
<span class="n">k</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s2">".*:"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
|
||||
<span class="n">exif_new</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
||||
<span class="n">exifdict</span> <span class="o">=</span> <span class="n">exif_new</span>
|
||||
|
||||
@@ -450,18 +612,18 @@
|
||||
<span class="k">return</span> <span class="n">exifdict</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExifTool.json"><a class="viewcode-back" href="../../reference.html#osxphotos.ExifTool.json">[docs]</a> <span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""returns JSON string containing all EXIF tags and values from exiftool"""</span>
|
||||
<span class="n">json</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-json"</span><span class="p">)</span>
|
||||
<span class="n">json</span> <span class="o">=</span> <span class="n">unescape_str</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
|
||||
<span class="sd">"""returns JSON string containing all EXIF tags and values from exiftool"""</span>
|
||||
<span class="n">json</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="s2">"-json"</span><span class="p">)</span>
|
||||
<span class="n">json</span> <span class="o">=</span> <span class="n">unescape_str</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
|
||||
<span class="k">return</span> <span class="n">json</span></div>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_read_exif</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""read exif data from file"""</span>
|
||||
<span class="sd">"""read exif data from file"""</span>
|
||||
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"file: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="si">}</span><span class="se">\n</span><span class="s2">exiftool: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span><span class="o">.</span><span class="n">_exiftool</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"file: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="si">}</span><span class="se">\n</span><span class="s2">exiftool: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_exiftoolproc</span><span class="o">.</span><span class="n">_exiftool</span><span class="si">}</span><span class="s2">"</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_context_mgr</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
@@ -477,15 +639,15 @@
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">ExifToolCaching</span><span class="p">(</span><span class="n">ExifTool</span><span class="p">):</span>
|
||||
<span class="sd">"""Basic exiftool interface for reading and writing EXIF tags, with caching.</span>
|
||||
<span class="sd"> Use this only when you know the file's EXIF data will not be changed by any external process.</span>
|
||||
<span class="sd">"""Basic exiftool interface for reading and writing EXIF tags, with caching.</span>
|
||||
<span class="sd"> Use this only when you know the file's EXIF data will not be changed by any external process.</span>
|
||||
|
||||
<span class="sd"> Creates a singleton cached ExifTool instance"""</span>
|
||||
<span class="sd"> Creates a singleton cached ExifTool instance"""</span>
|
||||
|
||||
<span class="n">_singletons</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""create new object or return instance of already created singleton"""</span>
|
||||
<span class="sd">"""create new object or return instance of already created singleton"""</span>
|
||||
<span class="k">if</span> <span class="n">filepath</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_singletons</span><span class="p">:</span>
|
||||
<span class="bp">cls</span><span class="o">.</span><span class="n">_singletons</span><span class="p">[</span><span class="n">filepath</span><span class="p">]</span> <span class="o">=</span> <span class="n">_ExifToolCaching</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_singletons</span><span class="p">[</span><span class="n">filepath</span><span class="p">]</span>
|
||||
@@ -493,7 +655,7 @@
|
||||
|
||||
<span class="k">class</span> <span class="nc">_ExifToolCaching</span><span class="p">(</span><span class="n">ExifTool</span><span class="p">):</span>
|
||||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""Create read-only ExifTool object that caches values</span>
|
||||
<span class="sd">"""Create read-only ExifTool object that caches values</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> file: path to image file</span>
|
||||
@@ -501,21 +663,21 @@
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> ExifTool instance</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_json_cache</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_asdict_cache</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">exiftool</span><span class="o">=</span><span class="n">exiftool</span><span class="p">,</span> <span class="n">overwrite</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">flags</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">run_commands</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">commands</span><span class="p">,</span> <span class="n">no_file</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="n">commands</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"-json"</span><span class="p">,</span> <span class="s2">"-ver"</span><span class="p">]:</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">commands</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"-json"</span><span class="p">,</span> <span class="s2">"-ver"</span><span class="p">]:</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">run_commands</span><span class="p">(</span><span class="o">*</span><span class="n">commands</span><span class="p">,</span> <span class="n">no_file</span><span class="o">=</span><span class="n">no_file</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">setvalue</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">addvalues</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="o">*</span><span class="n">values</span><span class="p">):</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2"> is read-only"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">_json_cache</span><span class="p">:</span>
|
||||
@@ -523,13 +685,13 @@
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_json_cache</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tag_groups</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">normalized</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="sd">"""return dictionary of all EXIF tags and values from exiftool</span>
|
||||
<span class="sd">"""return dictionary of all EXIF tags and values from exiftool</span>
|
||||
<span class="sd"> returns empty dict if no tags</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> tag_groups: if True (default), dict keys have tag groups, e.g. "IPTC:Keywords"; if False, drops groups from keys, e.g. "Keywords"</span>
|
||||
<span class="sd"> tag_groups: if True (default), dict keys have tag groups, e.g. "IPTC:Keywords"; if False, drops groups from keys, e.g. "Keywords"</span>
|
||||
<span class="sd"> normalized: if True, dict keys are all normalized to lower case (default is False)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_asdict_cache</span><span class="p">[</span><span class="n">tag_groups</span><span class="p">][</span><span class="n">normalized</span><span class="p">]</span>
|
||||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||||
@@ -541,76 +703,49 @@
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_asdict_cache</span><span class="p">[</span><span class="n">tag_groups</span><span class="p">][</span><span class="n">normalized</span><span class="p">]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">flush_cache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Clear cached data so that calls to json or asdict return fresh data"""</span>
|
||||
<span class="sd">"""Clear cached data so that calls to json or asdict return fresh data"""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_json_cache</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_asdict_cache</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
</pre></div>
|
||||
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
<div class="related-pages">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h1 class="logo"><a href="../../index.html">osxphotos</a></h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../overview.html">osxphotos</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../tutorial.html">Tutorial</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../cli.html">osxphotos command line interface (CLI)</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../reference.html">osxphotos package</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="../../index.html">Documentation overview</a><ul>
|
||||
<li><a href="../index.html">Module code</a><ul>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="bottom-of-page">
|
||||
<div class="left-details">
|
||||
<div class="copyright">
|
||||
Copyright © 2021, Rhet Turnbull
|
||||
</div>
|
||||
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
|
||||
|
||||
<a href="https://github.com/pradyunsg/furo">Furo</a>
|
||||
|
||||
</div>
|
||||
<div class="right-details">
|
||||
<div class="icons">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2021, Rhet Turnbull.
|
||||
<aside class="toc-drawer no-toc">
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 4.4.0</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script src="../../_static/scripts/furo.js"></script>
|
||||
<script src="../../_static/clipboard.min.js"></script>
|
||||
<script src="../../_static/copybutton.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.export_db - osxphotos 0.47.13 documentation</title>
|
||||
<title>osxphotos.export_db - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.47.13 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.47.13 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -197,18 +197,21 @@
|
||||
<span></span><span class="sd">""" Helper class for managing database used by PhotoExporter for tracking state of exports and updates """</span>
|
||||
|
||||
|
||||
<span class="kn">import</span> <span class="nn">contextlib</span>
|
||||
<span class="kn">import</span> <span class="nn">datetime</span>
|
||||
<span class="kn">import</span> <span class="nn">gzip</span>
|
||||
<span class="kn">import</span> <span class="nn">json</span>
|
||||
<span class="kn">import</span> <span class="nn">logging</span>
|
||||
<span class="kn">import</span> <span class="nn">os</span>
|
||||
<span class="kn">import</span> <span class="nn">pathlib</span>
|
||||
<span class="kn">import</span> <span class="nn">pickle</span>
|
||||
<span class="kn">import</span> <span class="nn">sqlite3</span>
|
||||
<span class="kn">import</span> <span class="nn">sys</span>
|
||||
<span class="kn">import</span> <span class="nn">time</span>
|
||||
<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">suppress</span>
|
||||
<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">StringIO</span>
|
||||
<span class="kn">from</span> <span class="nn">sqlite3</span> <span class="kn">import</span> <span class="n">Error</span>
|
||||
<span class="kn">from</span> <span class="nn">tempfile</span> <span class="kn">import</span> <span class="n">TemporaryDirectory</span>
|
||||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span>
|
||||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">Union</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">tenacity</span> <span class="kn">import</span> <span class="n">retry</span><span class="p">,</span> <span class="n">stop_after_attempt</span>
|
||||
|
||||
@@ -223,12 +226,42 @@
|
||||
<span class="s2">"ExportDBTemp"</span><span class="p">,</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="n">OSXPHOTOS_EXPORTDB_VERSION</span> <span class="o">=</span> <span class="s2">"6.0"</span>
|
||||
<span class="n">OSXPHOTOS_EXPORTDB_VERSION</span> <span class="o">=</span> <span class="s2">"7.0"</span>
|
||||
<span class="n">OSXPHOTOS_ABOUT_STRING</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"Created by osxphotos version </span><span class="si">{</span><span class="n">__version__</span><span class="si">}</span><span class="s2"> (https://github.com/RhetTbull/osxphotos) on </span><span class="si">{</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="si">}</span><span class="s2">"</span>
|
||||
|
||||
<span class="c1"># max retry attempts for methods which use tenacity.retry</span>
|
||||
<span class="n">MAX_RETRY_ATTEMPTS</span> <span class="o">=</span> <span class="mi">5</span>
|
||||
|
||||
<span class="c1"># maximum number of export results rows to save</span>
|
||||
<span class="n">MAX_EXPORT_RESULTS_DATA_ROWS</span> <span class="o">=</span> <span class="mi">10</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">pickle_and_zip</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Any</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bytes</span><span class="p">:</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Pickle and gzip data.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> data: data to pickle and gzip (must be pickle-able)</span>
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> bytes of gzipped pickled data</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">pickled</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">gzip</span><span class="o">.</span><span class="n">compress</span><span class="p">(</span><span class="n">pickled</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">unzip_and_unpickle</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-></span> <span class="n">Any</span><span class="p">:</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Unzip and unpickle data.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> data: data to unzip and unpickle</span>
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> unpickled data</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="n">pickle</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">gzip</span><span class="o">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="ExportDB"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB">[docs]</a><span class="k">class</span> <span class="nc">ExportDB</span><span class="p">:</span>
|
||||
<span class="sd">"""Interface to sqlite3 database used to store state information for osxphotos export command"""</span>
|
||||
@@ -388,6 +421,77 @@
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.set_export_results"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.set_export_results">[docs]</a> <span class="k">def</span> <span class="nf">set_export_results</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">results</span><span class="p">):</span>
|
||||
<span class="sd">"""Store export results in database; data is pickled and gzipped for storage"""</span>
|
||||
|
||||
<span class="n">results_data</span> <span class="o">=</span> <span class="n">pickle_and_zip</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
|
||||
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> UPDATE export_results_data</span>
|
||||
<span class="sd"> SET datetime = ?,</span>
|
||||
<span class="sd"> export_results = ?</span>
|
||||
<span class="sd"> WHERE datetime = (SELECT MIN(datetime) FROM export_results_data);</span>
|
||||
<span class="sd"> """</span><span class="p">,</span>
|
||||
<span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="n">results_data</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_export_results"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_export_results">[docs]</a> <span class="k">def</span> <span class="nf">get_export_results</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">run</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
|
||||
<span class="sd">"""Retrieve export results from database</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> run: which run to retrieve results for;</span>
|
||||
<span class="sd"> 0 = most recent run, -1 = previous run, -2 = run prior to that, etc.</span>
|
||||
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> ExportResults object or None if no results found</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">run</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"run must be 0 or negative"</span><span class="p">)</span>
|
||||
<span class="n">run</span> <span class="o">=</span> <span class="o">-</span><span class="n">run</span>
|
||||
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> SELECT export_results</span>
|
||||
<span class="sd"> FROM export_results_data</span>
|
||||
<span class="sd"> ORDER BY datetime DESC</span>
|
||||
<span class="sd"> """</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">rows</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">data</span> <span class="o">=</span> <span class="n">rows</span><span class="p">[</span><span class="n">run</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="n">unzip_and_unpickle</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">if</span> <span class="n">data</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||||
<span class="n">results</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">return</span> <span class="n">results</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.get_exported_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.get_exported_files">[docs]</a> <span class="k">def</span> <span class="nf">get_exported_files</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Returns tuple of (uuid, filepath) for all paths of all exported files tracked in the database"""</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_conn</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"SELECT uuid, filepath FROM export_data"</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="k">while</span> <span class="n">row</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">fetchone</span><span class="p">():</span>
|
||||
<span class="k">yield</span> <span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">export_dir</span><span class="p">,</span> <span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||||
<span class="k">return</span></div>
|
||||
|
||||
<div class="viewcode-block" id="ExportDB.close"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportDB.close">[docs]</a> <span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""close the database connection"""</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
@@ -405,7 +509,7 @@
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">dbfile</span><span class="p">):</span>
|
||||
<span class="n">conn</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_db_connection</span><span class="p">(</span><span class="n">dbfile</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">conn</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">"Error getting connection to database </span><span class="si">{dbfile}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Error getting connection to database </span><span class="si">{</span><span class="n">dbfile</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_create_or_migrate_db_tables</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_created</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">was_upgraded</span> <span class="o">=</span> <span class="p">()</span>
|
||||
@@ -558,12 +662,16 @@
|
||||
<span class="c1"># create export_data table</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_migrate_5_0_to_6_0</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">version</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="s2">"7.0"</span><span class="p">:</span>
|
||||
<span class="c1"># create report_data table</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_migrate_6_0_to_7_0</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
|
||||
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">"VACUUM;"</span><span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__del__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""ensure the database connection is closed"""</span>
|
||||
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">suppress</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||
<span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_insert_run_info</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
@@ -752,6 +860,29 @@
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_migrate_6_0_to_7_0</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">):</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">c</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""CREATE TABLE IF NOT EXISTS export_results_data (</span>
|
||||
<span class="sd"> id INTEGER PRIMARY KEY,</span>
|
||||
<span class="sd"> datetime TEXT,</span>
|
||||
<span class="sd"> export_results BLOB</span>
|
||||
<span class="sd"> );"""</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># pre-populate report_data table with blank fields</span>
|
||||
<span class="c1"># ExportDB will use these as circular buffer always writing to the oldest record</span>
|
||||
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">MAX_EXPORT_RESULTS_DATA_ROWS</span><span class="p">):</span>
|
||||
<span class="n">c</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span>
|
||||
<span class="sd">"""INSERT INTO export_results_data (datetime, export_results) VALUES (?, ?);"""</span><span class="p">,</span>
|
||||
<span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">(),</span> <span class="sa">b</span><span class="s2">""</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># sleep a tiny bit just to ensure time stamps increment</span>
|
||||
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>
|
||||
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_perform_db_maintenace</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">):</span>
|
||||
<span class="sd">"""Perform database maintenance"""</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
@@ -826,7 +957,7 @@
|
||||
<span class="k">except</span> <span class="n">Error</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="n">logging</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_open_export_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dbfile</span><span class="p">):</span>
|
||||
<span class="k">def</span> <span class="nf">_open_export_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dbfile</span><span class="p">):</span> <span class="c1"># sourcery skip: raise-specific-error</span>
|
||||
<span class="sd">"""open export database and return a db connection</span>
|
||||
<span class="sd"> returns: connection to the database</span>
|
||||
<span class="sd"> """</span>
|
||||
@@ -877,7 +1008,7 @@
|
||||
|
||||
<span class="k">def</span> <span class="fm">__del__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""close the database connection"""</span>
|
||||
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">suppress</span><span class="p">(</span><span class="n">Error</span><span class="p">):</span>
|
||||
<span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="n">Error</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
|
||||
|
||||
@@ -1129,6 +1260,10 @@
|
||||
<span class="s2">"photoinfo"</span><span class="p">:</span> <span class="n">photoinfo</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">json</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""Return json of self"""</span>
|
||||
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">asdict</span><span class="p">(),</span> <span class="n">indent</span><span class="o">=</span><span class="n">indent</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="fm">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_context_manager</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="k">return</span> <span class="bp">self</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.48.3 documentation</title>
|
||||
<title>osxphotos.photoexporter - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.3 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.3 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -206,6 +206,7 @@
|
||||
<span class="kn">import</span> <span class="nn">typing</span> <span class="k">as</span> <span class="nn">t</span>
|
||||
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span> <span class="c1"># pylint: disable=syntax-error</span>
|
||||
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">asdict</span><span class="p">,</span> <span class="n">dataclass</span>
|
||||
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
|
||||
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">photoscript</span>
|
||||
@@ -434,59 +435,62 @@
|
||||
|
||||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">exported</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">new</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exif_updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">touched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">to_touch</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">converted_to_jpeg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_json_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">missing</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exiftool_warning</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">deleted_files</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">deleted_directories</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">deleted_files</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exif_updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exiftool_error</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exiftool_warning</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exported_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">exported</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">metadata_changed</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">missing_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">missing</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">new</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_exiftool_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_exiftool_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_json_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_json_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_xmp_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">sidecar_xmp_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">skipped_album</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">to_touch</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">touched</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">updated</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_skipped</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">xattr_written</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span> <span class="o">=</span> <span class="n">exported</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">new</span> <span class="o">=</span> <span class="n">new</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">updated</span> <span class="o">=</span> <span class="n">updated</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">skipped</span> <span class="o">=</span> <span class="n">skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span> <span class="o">=</span> <span class="n">exif_updated</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">touched</span> <span class="o">=</span> <span class="n">touched</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">to_touch</span> <span class="o">=</span> <span class="n">to_touch</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">converted_to_jpeg</span> <span class="o">=</span> <span class="n">converted_to_jpeg</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span> <span class="o">=</span> <span class="n">sidecar_json_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span> <span class="o">=</span> <span class="n">sidecar_json_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span> <span class="o">=</span> <span class="n">sidecar_exiftool_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span> <span class="o">=</span> <span class="n">sidecar_exiftool_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span> <span class="o">=</span> <span class="n">sidecar_xmp_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span> <span class="o">=</span> <span class="n">sidecar_xmp_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">missing</span> <span class="o">=</span> <span class="n">missing</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span> <span class="o">=</span> <span class="n">exiftool_warning</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">=</span> <span class="n">exiftool_error</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_written</span> <span class="o">=</span> <span class="n">xattr_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_skipped</span> <span class="o">=</span> <span class="n">xattr_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">=</span> <span class="n">deleted_files</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_directories</span> <span class="o">=</span> <span class="n">deleted_directories</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">deleted_files</span> <span class="o">=</span> <span class="n">deleted_files</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exif_updated</span> <span class="o">=</span> <span class="n">exif_updated</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_error</span> <span class="o">=</span> <span class="n">exiftool_error</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exiftool_warning</span> <span class="o">=</span> <span class="n">exiftool_warning</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exported</span> <span class="o">=</span> <span class="n">exported</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">exported_album</span> <span class="o">=</span> <span class="n">exported_album</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">skipped_album</span> <span class="o">=</span> <span class="n">skipped_album</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">missing_album</span> <span class="o">=</span> <span class="n">missing_album</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">=</span> <span class="n">metadata_changed</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">missing</span> <span class="o">=</span> <span class="n">missing</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">missing_album</span> <span class="o">=</span> <span class="n">missing_album</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">new</span> <span class="o">=</span> <span class="n">new</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_skipped</span> <span class="o">=</span> <span class="n">sidecar_exiftool_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_exiftool_written</span> <span class="o">=</span> <span class="n">sidecar_exiftool_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_skipped</span> <span class="o">=</span> <span class="n">sidecar_json_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_json_written</span> <span class="o">=</span> <span class="n">sidecar_json_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_skipped</span> <span class="o">=</span> <span class="n">sidecar_xmp_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sidecar_xmp_written</span> <span class="o">=</span> <span class="n">sidecar_xmp_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">skipped</span> <span class="o">=</span> <span class="n">skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">skipped_album</span> <span class="o">=</span> <span class="n">skipped_album</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">to_touch</span> <span class="o">=</span> <span class="n">to_touch</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">touched</span> <span class="o">=</span> <span class="n">touched</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">updated</span> <span class="o">=</span> <span class="n">updated</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_skipped</span> <span class="o">=</span> <span class="n">xattr_skipped</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xattr_written</span> <span class="o">=</span> <span class="n">xattr_written</span> <span class="ow">or</span> <span class="p">[]</span>
|
||||
|
||||
<div class="viewcode-block" id="ExportResults.all_files"><a class="viewcode-back" href="../../reference.html#osxphotos.ExportResults.all_files">[docs]</a> <span class="k">def</span> <span class="nf">all_files</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""return all filenames contained in results"""</span>
|
||||
@@ -544,7 +548,8 @@
|
||||
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="p">(</span>
|
||||
<span class="s2">"ExportResults("</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">"exported=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exported</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">"datetime=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">datetime</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",exported=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">exported</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",new=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">new</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",updated=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">updated</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="o">+</span> <span class="sa">f</span><span class="s2">",skipped=</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">skipped</span><span class="si">}</span><span class="s2">"</span>
|
||||
@@ -926,7 +931,7 @@
|
||||
<span class="k">return</span> <span class="n">ShouldUpdate</span><span class="o">.</span><span class="n">EXPORT_OPTIONS_DIFFERENT</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
||||
<span class="n">current_exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">current_exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">rv</span> <span class="o">=</span> <span class="n">current_exifdata</span> <span class="o">!=</span> <span class="n">file_record</span><span class="o">.</span><span class="n">exifdata</span>
|
||||
<span class="c1"># if using exiftool, don't need to continue checking edited below</span>
|
||||
<span class="c1"># as exiftool will be used to update edited file</span>
|
||||
@@ -1334,7 +1339,7 @@
|
||||
<span class="c1"># point src to the tmp_file so that the original source is not modified</span>
|
||||
<span class="c1"># and the export grabs the new file</span>
|
||||
<span class="n">src</span> <span class="o">=</span> <span class="n">tmp_file</span>
|
||||
<span class="n">exif_results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_write_exif_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="n">exif_results</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="n">src</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
@@ -1376,7 +1381,7 @@
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_signature</span><span class="p">:</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">dest_sig</span> <span class="o">=</span> <span class="n">fileutil</span><span class="o">.</span><span class="n">file_sig</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">exiftool</span><span class="p">:</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">exifdata</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span> <span class="o">!=</span> <span class="n">rec</span><span class="o">.</span><span class="n">digest</span><span class="p">:</span>
|
||||
<span class="n">results</span><span class="o">.</span><span class="n">metadata_changed</span> <span class="o">=</span> <span class="p">[</span><span class="n">dest_str</span><span class="p">]</span>
|
||||
<span class="n">rec</span><span class="o">.</span><span class="n">digest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">hexdigest</span>
|
||||
@@ -1511,7 +1516,7 @@
|
||||
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
@@ -1528,7 +1533,7 @@
|
||||
<span class="n">sidecar_filename</span> <span class="o">=</span> <span class="n">dest</span><span class="o">.</span><span class="n">parent</span> <span class="o">/</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">dest</span><span class="o">.</span><span class="n">stem</span><span class="si">}{</span><span class="n">dest_suffix</span><span class="si">}</span><span class="s2">.json"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">sidecar_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="n">tag_groups</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="n">dest</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">sidecars</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
@@ -1627,19 +1632,20 @@
|
||||
|
||||
<span class="k">return</span> <span class="n">results</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_write_exif_metadata_to_file</span><span class="p">(</span>
|
||||
<div class="viewcode-block" id="PhotoExporter.write_exiftool_metadata_to_file"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file">[docs]</a> <span class="k">def</span> <span class="nf">write_exiftool_metadata_to_file</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">src</span><span class="p">,</span>
|
||||
<span class="n">dest</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">,</span>
|
||||
<span class="p">)</span> <span class="o">-></span> <span class="n">ExportResults</span><span class="p">:</span>
|
||||
<span class="sd">"""Write exif metadata to file using exiftool</span>
|
||||
<span class="sd">"""Write exif metadata to src file using exiftool</span>
|
||||
|
||||
<span class="sd"> Note: this method modifies src so src must be a copy of the original file;</span>
|
||||
<span class="sd"> Caution: This method modifies *src*, not *dest*, </span>
|
||||
<span class="sd"> so src must be a copy of the original file if you don't want the source modified;</span>
|
||||
<span class="sd"> it also does not write to dest (dest is the intended destination for purposes of</span>
|
||||
<span class="sd"> referencing the export database. This allows the exiftool update to be done on the</span>
|
||||
<span class="sd"> local machine prior to being copied to the export destination which may be on a</span>
|
||||
<span class="sd"> network drive or other slower external storage."""</span>
|
||||
<span class="sd"> network drive or other slower external storage)."""</span>
|
||||
|
||||
<span class="n">verbose</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">verbose</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_verbose</span>
|
||||
<span class="n">exiftool_results</span> <span class="o">=</span> <span class="n">ExportResults</span><span class="p">()</span>
|
||||
@@ -1671,7 +1677,7 @@
|
||||
|
||||
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">exif_updated</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="n">exiftool_results</span><span class="o">.</span><span class="n">to_touch</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">exiftool_results</span>
|
||||
<span class="k">return</span> <span class="n">exiftool_results</span></div>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_should_run_exiftool</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">options</span><span class="p">:</span> <span class="n">ExportOptions</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||
<span class="sd">"""Return True if exiftool should be run to update metadata"""</span>
|
||||
@@ -1682,7 +1688,7 @@
|
||||
<span class="n">old_data</span> <span class="o">=</span> <span class="n">exif_record</span><span class="o">.</span><span class="n">exifdata</span> <span class="k">if</span> <span class="n">exif_record</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
<span class="k">if</span> <span class="n">old_data</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="n">old_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">old_data</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">))</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">exiftool_json_sidecar</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">))</span>
|
||||
<span class="n">current_data</span> <span class="o">=</span> <span class="n">current_data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">old_data</span> <span class="o">!=</span> <span class="n">current_data</span><span class="p">:</span>
|
||||
<span class="n">files_are_different</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
@@ -2022,7 +2028,7 @@
|
||||
<span class="k">pass</span>
|
||||
<span class="k">return</span> <span class="n">persons</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<div class="viewcode-block" id="PhotoExporter.exiftool_json_sidecar"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">[docs]</a> <span class="k">def</span> <span class="nf">exiftool_json_sidecar</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
<span class="n">options</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Optional</span><span class="p">[</span><span class="n">ExportOptions</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">tag_groups</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">,</span>
|
||||
@@ -2074,7 +2080,7 @@
|
||||
<span class="n">exif_new</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
||||
<span class="n">exif</span> <span class="o">=</span> <span class="n">exif_new</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">exif</span><span class="p">])</span>
|
||||
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">exif</span><span class="p">])</span></div>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_xmp_sidecar</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="p">,</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.photoinfo - osxphotos 0.48.3 documentation</title>
|
||||
<title>osxphotos.photoinfo - osxphotos 0.48.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.3 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.3 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -1089,38 +1089,37 @@
|
||||
|
||||
<span class="k">return</span> <span class="n">photopath</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="nd">@cached_property</span>
|
||||
<span class="k">def</span> <span class="nf">path_derivatives</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return any derivative (preview) images associated with the photo as a list of paths, sorted by file size (largest first)"""</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_4</span><span class="p">()</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_db_version</span> <span class="o"><=</span> <span class="n">_PHOTOS_4_VERSION</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_4</span><span class="p">()</span>
|
||||
|
||||
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
|
||||
<span class="o">/</span> <span class="s2">"resources"</span>
|
||||
<span class="o">/</span> <span class="s2">"derivatives"</span>
|
||||
<span class="o">/</span> <span class="n">directory</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="n">derivative_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">*.*"</span><span class="p">)</span>
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
|
||||
<span class="c1"># return list of filename but skip .THM files (these are actually low-res thumbnails in JPEG format but with .THM extension)</span>
|
||||
<span class="n">derivatives</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span> <span class="k">if</span> <span class="n">filename</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="s2">".THM"</span>
|
||||
<span class="p">]</span>
|
||||
<span class="k">if</span> <span class="p">(</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span>
|
||||
<span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">derivatives</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span>
|
||||
<span class="ow">and</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".mov"</span><span class="p">)</span>
|
||||
<span class="p">):</span>
|
||||
<span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shared</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives_5_shared</span><span class="p">()</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives</span> <span class="o">=</span> <span class="n">derivatives</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_path_derivatives</span>
|
||||
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span> <span class="o">/</span> <span class="sa">f</span><span class="s2">"resources/derivatives/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">derivative_path</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">*.*"</span><span class="p">))</span>
|
||||
|
||||
<span class="c1"># previews may be missing from derivatives path</span>
|
||||
<span class="c1"># there are what appear to be low res thumbnails in the "masters" subfolder</span>
|
||||
<span class="n">thumb_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
|
||||
<span class="o">/</span> <span class="sa">f</span><span class="s2">"resources/derivatives/masters/</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">_4_5005_c.jpeg"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">thumb_path</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
||||
<span class="n">files</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">thumb_path</span><span class="p">)</span>
|
||||
|
||||
<span class="n">files</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="n">f</span><span class="o">.</span><span class="n">stat</span><span class="p">()</span><span class="o">.</span><span class="n">st_size</span><span class="p">)</span>
|
||||
<span class="c1"># return list of filename but skip .THM files (these are actually low-res thumbnails in JPEG format but with .THM extension)</span>
|
||||
<span class="n">derivatives</span> <span class="o">=</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">files</span> <span class="k">if</span> <span class="n">filename</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="s2">".THM"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">isphoto</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">derivatives</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".mov"</span><span class="p">):</span>
|
||||
<span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">derivatives</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">derivatives</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_path_derivatives_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return paths to all derivative (preview) files for Photos <= 4"""</span>
|
||||
@@ -1130,10 +1129,7 @@
|
||||
<span class="n">folder_id</span><span class="p">,</span> <span class="n">file_id</span> <span class="o">=</span> <span class="n">_get_resource_loc</span><span class="p">(</span><span class="n">modelid</span><span class="p">)</span>
|
||||
<span class="n">derivatives_root</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
|
||||
<span class="o">/</span> <span class="s2">"resources"</span>
|
||||
<span class="o">/</span> <span class="s2">"proxies"</span>
|
||||
<span class="o">/</span> <span class="s2">"derivatives"</span>
|
||||
<span class="o">/</span> <span class="n">folder_id</span>
|
||||
<span class="o">/</span> <span class="sa">f</span><span class="s2">"resources/proxies/derivatives/</span><span class="si">{</span><span class="n">folder_id</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># photos appears to usually be in "00" subfolder but</span>
|
||||
@@ -1158,6 +1154,19 @@
|
||||
<span class="c1"># didn't find a derivatives path</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_path_derivatives_5_shared</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Return paths to all derivative (preview) files for shared iCloud photos in Photos >= 5"""</span>
|
||||
<span class="n">directory</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_uuid</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># first char of uuid</span>
|
||||
<span class="c1"># only 1 derivative for shared photos and it's called 'UUID_4_5005_c.jpeg'</span>
|
||||
<span class="n">derivative_path</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_db</span><span class="o">.</span><span class="n">_library_path</span><span class="p">)</span>
|
||||
<span class="o">/</span> <span class="s2">"resources/cloudsharing/resources/derivatives/masters"</span>
|
||||
<span class="o">/</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">directory</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">_4_5005_c.jpeg"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">derivative_path</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
|
||||
<span class="k">return</span> <span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">derivative_path</span><span class="p">)]</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">panorama</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""Returns True if photo is a panorama, otherwise False"""</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.photosdb.photosdb - osxphotos 0.48.3 documentation</title>
|
||||
<title>osxphotos.photosdb.photosdb - osxphotos 0.48.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../../index.html"><div class="brand">osxphotos 0.48.3 documentation</div></a>
|
||||
<a href="../../../index.html"><div class="brand">osxphotos 0.48.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.3 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -3272,6 +3272,8 @@
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="n">_get_photos_by_attribute</span><span class="p">(</span>
|
||||
<span class="n">photos</span><span class="p">,</span> <span class="s2">"keywords"</span><span class="p">,</span> <span class="n">keyword</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_case</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">no_keyword</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">p</span><span class="o">.</span><span class="n">keywords</span><span class="p">]</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">person</span><span class="p">:</span>
|
||||
<span class="n">photos</span> <span class="o">=</span> <span class="n">_get_photos_by_attribute</span><span class="p">(</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.phototemplate - osxphotos 0.48.3 documentation</title>
|
||||
<title>osxphotos.phototemplate - osxphotos 0.49.0 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.3 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.49.0 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.3 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.0 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -196,14 +196,14 @@
|
||||
<h1>Source code for osxphotos.phototemplate</h1><div class="highlight"><pre>
|
||||
<span></span><span class="sd">""" Custom template system for osxphotos, implements metadata template language (MTL) """</span>
|
||||
|
||||
|
||||
<span class="kn">import</span> <span class="nn">datetime</span>
|
||||
<span class="kn">import</span> <span class="nn">json</span>
|
||||
<span class="kn">import</span> <span class="nn">locale</span>
|
||||
<span class="kn">import</span> <span class="nn">logging</span>
|
||||
<span class="kn">import</span> <span class="nn">os</span>
|
||||
<span class="kn">import</span> <span class="nn">pathlib</span>
|
||||
<span class="kn">import</span> <span class="nn">shlex</span>
|
||||
<span class="kn">import</span> <span class="nn">sys</span>
|
||||
<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">suppress</span>
|
||||
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
|
||||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span>
|
||||
|
||||
@@ -615,7 +615,7 @@
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">template</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="n">TextXSyntaxError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"SyntaxError: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"SyntaxError: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">model</span><span class="p">:</span>
|
||||
<span class="c1"># empty string</span>
|
||||
@@ -808,10 +808,12 @@
|
||||
<span class="k">break</span>
|
||||
<span class="k">if</span> <span class="n">match</span><span class="p">:</span>
|
||||
<span class="k">break</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="p">[</span><span class="s2">"True"</span><span class="p">]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
|
||||
<span class="k">return</span> <span class="p">(</span>
|
||||
<span class="p">[</span><span class="s2">"True"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
|
||||
<span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">comparison_test</span><span class="p">(</span><span class="n">test_function</span><span class="p">):</span>
|
||||
<span class="sd">"""Perform numerical comparisons using test_function; closure to capture conditional_val, vals, negation"""</span>
|
||||
@@ -823,14 +825,16 @@
|
||||
<span class="n">match</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">(</span>
|
||||
<span class="n">test_function</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="n">vals</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">float</span><span class="p">(</span><span class="n">conditional_value</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="p">[</span><span class="s2">"True"</span><span class="p">]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
|
||||
<span class="k">return</span> <span class="p">(</span>
|
||||
<span class="p">[</span><span class="s2">"True"</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">negation</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">negation</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">match</span><span class="p">)</span>
|
||||
<span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">operator</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"contains"</span><span class="p">,</span> <span class="s2">"matches"</span><span class="p">,</span> <span class="s2">"startswith"</span><span class="p">,</span> <span class="s2">"endswith"</span><span class="p">]:</span>
|
||||
<span class="c1"># process any "or" values separated by "|"</span>
|
||||
@@ -918,9 +922,6 @@
|
||||
<span class="sd"> ValueError if no rule exists for field.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
|
||||
<span class="c1"># initialize today with current date/time if needed</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">today</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
|
||||
@@ -928,7 +929,50 @@
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
|
||||
<span class="c1"># wouldn't a switch/case statement be nice...</span>
|
||||
<span class="k">if</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"name"</span><span class="p">:</span>
|
||||
<span class="c1"># handle the fields that don't require a PhotoInfo object first</span>
|
||||
<span class="k">if</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.date"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">date</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.year"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">year</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.yy"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">yy</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.mm"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">mm</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.month"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">month</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.mon"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">mon</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.dd"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">dd</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.dow"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">dow</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.doy"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">doy</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.hour"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">hour</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.min"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">min</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.sec"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">sec</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.strftime"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">default</span><span class="p">:</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="n">default</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="k">except</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid strftime template: '</span><span class="si">{</span><span class="n">default</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">PUNCTUATION</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">PUNCTUATION</span><span class="p">[</span><span class="n">field</span><span class="p">]</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"osxphotos_version"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">__version__</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"osxphotos_cmd_line"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="c1"># if no uuid, don't have a PhotoInfo object (could be PhotoInfoNone)</span>
|
||||
<span class="c1"># so don't try to handle any of the photo fields</span>
|
||||
<span class="k">return</span> <span class="p">[]</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"name"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"original_name"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="p">)</span><span class="o">.</span><span class="n">stem</span>
|
||||
@@ -1061,38 +1105,6 @@
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid strftime template: '</span><span class="si">{</span><span class="n">default</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.date"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">date</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.year"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">year</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.yy"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">yy</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.mm"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">mm</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.month"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">month</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.mon"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">mon</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.dd"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">dd</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.dow"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">dow</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.doy"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">doy</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.hour"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">hour</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.min"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">min</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.sec"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">DateTimeFormatter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">sec</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"today.strftime"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">default</span><span class="p">:</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">today</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="n">default</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="k">except</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Invalid strftime template: '</span><span class="si">{</span><span class="n">default</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"place.name"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">place</span><span class="o">.</span><span class="n">name</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">place</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"place.country_code"</span><span class="p">:</span>
|
||||
@@ -1178,33 +1190,25 @@
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"id"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">format_str_value</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="o">.</span><span class="n">_info</span><span class="p">[</span><span class="s2">"pk"</span><span class="p">],</span> <span class="n">subfield</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"album_seq"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"folder_album_seq"</span><span class="p">):</span>
|
||||
<span class="n">dest_path</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">dest_path</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">dest_path</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">dest_path</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"album_seq"</span><span class="p">):</span>
|
||||
<span class="n">album</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">dest_path</span><span class="p">)</span><span class="o">.</span><span class="n">name</span>
|
||||
<span class="n">album_info</span> <span class="o">=</span> <span class="n">_get_album_by_name</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">album</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">album_info</span> <span class="o">=</span> <span class="n">_get_album_by_path</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">,</span> <span class="n">dest_path</span><span class="p">)</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">album_info</span><span class="o">.</span><span class="n">photo_index</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">photo</span><span class="p">)</span> <span class="k">if</span> <span class="n">album_info</span> <span class="k">else</span> <span class="kc">None</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">IndexError</span><span class="p">):</span>
|
||||
<span class="n">start_id</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="n">start_id</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||||
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
|
||||
<span class="k">pass</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">format_str_value</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">subfield</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">PUNCTUATION</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">PUNCTUATION</span><span class="p">[</span><span class="n">field</span><span class="p">]</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"osxphotos_version"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">__version__</span>
|
||||
<span class="k">elif</span> <span class="n">field</span> <span class="o">==</span> <span class="s2">"osxphotos_cmd_line"</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="s2">" "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># if here, didn't get a match</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># sanitize filename or directory name if needed</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">sanitize_pathpart</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">dirname</span><span class="p">:</span>
|
||||
@@ -1234,8 +1238,8 @@
|
||||
<span class="n">field_value</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">field_value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field_stem</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unknown path-like field: </span><span class="si">{</span><span class="n">field_stem</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unknown path-like field: </span><span class="si">{</span><span class="n">field_stem</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
||||
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">_get_pathlib_value</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">field_value</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">quote</span><span class="p">)</span>
|
||||
|
||||
@@ -1279,14 +1283,14 @@
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"{"</span> <span class="o">+</span> <span class="n">values</span> <span class="o">+</span> <span class="s2">"}"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"parens"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"("</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">")"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">)"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"("</span> <span class="o">+</span> <span class="n">values</span> <span class="o">+</span> <span class="s2">")"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">values</span><span class="si">}</span><span class="s2">)"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"brackets"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"["</span> <span class="o">+</span> <span class="n">v</span> <span class="o">+</span> <span class="s2">"]"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s2">]"</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"["</span> <span class="o">+</span> <span class="n">values</span> <span class="o">+</span> <span class="s2">"]"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="n">values</span><span class="si">}</span><span class="s2">]"</span><span class="p">]</span> <span class="k">if</span> <span class="n">values</span> <span class="k">else</span> <span class="p">[]</span>
|
||||
<span class="k">elif</span> <span class="n">filter_</span> <span class="o">==</span> <span class="s2">"shell_quote"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">values</span> <span class="ow">and</span> <span class="nb">type</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="o">==</span> <span class="nb">list</span><span class="p">:</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="p">[</span><span class="n">shlex</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
||||
@@ -1602,7 +1606,7 @@
|
||||
<span class="o">*</span><span class="n">TEMPLATE_SUBSTITUTIONS_MULTI_VALUED</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span>
|
||||
<span class="p">]:</span>
|
||||
<span class="c1"># replace '|' with '\|' to avoid markdown parsing issues (e.g. in {pipe} description)</span>
|
||||
<span class="n">descr</span> <span class="o">=</span> <span class="n">descr</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"'|'"</span><span class="p">,</span> <span class="s2">"'\|'"</span><span class="p">)</span>
|
||||
<span class="n">descr</span> <span class="o">=</span> <span class="n">descr</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"'|'"</span><span class="p">,</span> <span class="sa">r</span><span class="s2">"'\|'"</span><span class="p">)</span>
|
||||
<span class="n">template_table</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">"</span><span class="se">\n</span><span class="s2">|</span><span class="si">{</span><span class="n">subst</span><span class="si">}</span><span class="s2">|</span><span class="si">{</span><span class="n">descr</span><span class="si">}</span><span class="s2">|"</span>
|
||||
<span class="k">return</span> <span class="n">template_table</span>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos.queryoptions - osxphotos 0.48.3 documentation</title>
|
||||
<title>osxphotos.queryoptions - osxphotos 0.48.7 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||
@@ -123,7 +123,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.3 documentation</div></a>
|
||||
<a href="../../index.html"><div class="brand">osxphotos 0.48.7 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.3 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.7 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -252,6 +252,7 @@
|
||||
<span class="sd"> no_description: search for photos with no description</span>
|
||||
<span class="sd"> no_likes: search for shared photos with no likes</span>
|
||||
<span class="sd"> no_location: search for photos with no location</span>
|
||||
<span class="sd"> no_keyword: search for photos with no keywords</span>
|
||||
<span class="sd"> no_place: search for photos with no place</span>
|
||||
<span class="sd"> no_title: search for photos with no title</span>
|
||||
<span class="sd"> not_burst: search for non-burst photos</span>
|
||||
@@ -332,6 +333,7 @@
|
||||
<span class="n">no_description</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">no_likes</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">no_location</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">no_keyword</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">no_place</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">no_title</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">not_burst</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
|
||||
@@ -320,7 +320,7 @@ Template Substitutions
|
||||
* - {crlf}
|
||||
- a carriage return + line feed: '\r\n'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.48.6'
|
||||
- The osxphotos version, e.g. '0.49.2'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
@@ -195,7 +195,7 @@ Photos tracks a tremendous amount of metadata associated with photos in the libr
|
||||
|
||||
``osxphotos export /path/to/export --exiftool``
|
||||
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with ``--exiftool`` to modify the metadata that is written by ``exiftool``. For example, you can use the ``--keyword-template`` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchal keywords in the format used by Lightroom Classic:
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with ``--exiftool`` to modify the metadata that is written by ``exiftool``. For example, you can use the ``--keyword-template`` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@@ -209,7 +209,7 @@ This will write basic metadata such as keywords, persons, and GPS location to th
|
||||
for joining the folders and albums. For example,
|
||||
if photo is in Folder1/Folder2/Album, (>) produces
|
||||
"Folder1>Folder2>Album" which some programs, such as
|
||||
Lightroom Classic, treat as hierarchal keywords
|
||||
Lightroom Classic, treat as hierarchical keywords
|
||||
|
||||
|
||||
The above command will write all the regular metadata that ``--exiftool`` normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form "Folder1>Folder2>Album". If you did not include the ``(>)`` in the template string (e.g. ``{folder_album}``\ ), folder_album would render in form "Folder1/Folder2/Album".
|
||||
|
||||
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.48.6',
|
||||
VERSION: '0.49.2',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
269
docs/cli.html
269
docs/cli.html
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.48.6 documentation</title>
|
||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -343,6 +343,138 @@ library specified by –db to the database specified by DB2</p>
|
||||
<dd><p>Optional argument(s)</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-exiftool">
|
||||
<h3>exiftool<a class="headerlink" href="#osxphotos-exiftool" title="Permalink to this headline">#</a></h3>
|
||||
<p>Run exiftool on previously exported files to update metadata.</p>
|
||||
<p>If you previously exported photos with <cite>osxphotos export</cite> but did not include the
|
||||
<cite>–exiftool</cite> option and you now want to update the metadata of the exported files with
|
||||
exiftool, you can use this command to do so.</p>
|
||||
<p>If you simply re-run the <cite>osxphotos export</cite> with <cite>–update</cite> and <cite>–exiftool</cite>, osxphotos will
|
||||
re-export all photos because it will detect that the previously exported photos do not have the
|
||||
exiftool metadata updates. This command will run exiftool on the previously exported photos
|
||||
to update all metadata then will update the export database so that using <cite>–exiftool –update</cite>
|
||||
with <cite>osxphotos export</cite> in the future will work correctly and not unnecessarily re-export photos.</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos exiftool <span class="o">[</span>OPTIONS<span class="o">]</span> EXPORT_DIRECTORY
|
||||
</pre></div>
|
||||
</div>
|
||||
<p class="rubric">Options</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-db-config">
|
||||
<span class="sig-name descname"><span class="pre">--db-config</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-db-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Load configuration options from the export database to match the last export; If any other command line options are used in conjunction with –db-config, they will override the corresponding values loaded from the export database; see also –load-config.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-load-config">
|
||||
<span class="sig-name descname"><span class="pre">--load-config</span></span><span class="sig-prename descclassname"> <span class="pre"><CONFIG_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-load-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Load options from file as written with –save-config. If any other command line options are used in conjunction with –load-config, they will override the corresponding values in the config file; see also –db-config.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-save-config">
|
||||
<span class="sig-name descname"><span class="pre">--save-config</span></span><span class="sig-prename descclassname"> <span class="pre"><CONFIG_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-save-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Save options to file for use with –load-config. File format is TOML.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-path">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-path</span></span><span class="sig-prename descclassname"> <span class="pre"><EXIFTOOL_PATH></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-path" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optionally specify path to exiftool; if not provided, will look for exiftool in $PATH.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-option">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-option</span></span><span class="sig-prename descclassname"> <span class="pre"><OPTION></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-option" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional flag/option to pass to exiftool when using –exiftool. For example, –exiftool-option ‘-m’ to ignore minor warnings. Specify these as you would on the exiftool command line. See exiftool docs at <a class="reference external" href="https://exiftool.org/exiftool_pod.html">https://exiftool.org/exiftool_pod.html</a> for full list of options. More than one option may be specified by repeating the option, e.g. –exiftool-option ‘-m’ –exiftool-option ‘-F’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-merge-keywords">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-merge-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-merge-keywords" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Merge any keywords found in the original file with keywords used for ‘–exiftool’ and ‘–sidecar’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exiftool-merge-persons">
|
||||
<span class="sig-name descname"><span class="pre">--exiftool-merge-persons</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exiftool-merge-persons" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Merge any persons found in the original file with persons used for ‘–exiftool’ and ‘–sidecar’.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-ignore-date-modified">
|
||||
<span class="sig-name descname"><span class="pre">--ignore-date-modified</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-ignore-date-modified" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –exiftool or –sidecar, will ignore the photo modification date and set EXIF:ModifyDate to EXIF:DateTimeOriginal; this is consistent with how Photos handles the EXIF:ModifyDate tag.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-person-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--person-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-person-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Use person in image as keyword/tag when exporting metadata.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-album-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--album-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-album-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Use album name as keyword/tag when exporting metadata.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-keyword-template">
|
||||
<span class="sig-name descname"><span class="pre">--keyword-template</span></span><span class="sig-prename descclassname"> <span class="pre"><TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-keyword-template" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>For use with –exiftool, –sidecar; specify a template string to use as keyword in the form ‘{name,DEFAULT}’ This is the same format as –directory. For example, if you wanted to add the full path to the folder and album photo is contained in as a keyword when exporting you could specify –keyword-template “{folder_album}” You may specify more than one template, for example –keyword-template “{folder_album}” –keyword-template “{created.year}”. See ‘–replace-keywords’ and Templating System below.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-replace-keywords">
|
||||
<span class="sig-name descname"><span class="pre">--replace-keywords</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-replace-keywords" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Replace keywords with any values specified with –keyword-template. By default, –keyword-template will add keywords to any keywords already associated with the photo. If –replace-keywords is specified, values from –keyword-template will replace any existing keywords instead of adding additional keywords.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-description-template">
|
||||
<span class="sig-name descname"><span class="pre">--description-template</span></span><span class="sig-prename descclassname"> <span class="pre"><TEMPLATE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-description-template" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>For use with –exiftool, –sidecar; specify a template string to use as description in the form ‘{name,DEFAULT}’ This is the same format as –directory. For example, if you wanted to append ‘exported with osxphotos on [today’s date]’ to the description, you could specify –description-template “{descr} exported with osxphotos on {today.date}” See Templating System below.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-exportdb">
|
||||
<span class="sig-name descname"><span class="pre">--exportdb</span></span><span class="sig-prename descclassname"> <span class="pre"><exportdb></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-exportdb" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional path to export database (if not in the default location in the export directory).</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-report">
|
||||
<span class="sig-name descname"><span class="pre">--report</span></span><span class="sig-prename descclassname"> <span class="pre"><REPORT_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-report" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Write a report of all files that were exported. The extension of the report filename will be used to determine the format. Valid extensions are: .csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). REPORT_FILE may be a template string (see Templating System), for example, –report ‘export_{today.date}.csv’ will write a CSV report file named with today’s date. See also –append.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-append">
|
||||
<span class="sig-name descname"><span class="pre">--append</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-append" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –report, add data to existing report file instead of overwriting it. See also –report.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-V">
|
||||
<span id="cmdoption-osxphotos-exiftool-v"></span><span id="cmdoption-osxphotos-exiftool-verbose"></span><span class="sig-name descname"><span class="pre">-V</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--verbose</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-V" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print verbose output.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-timestamp">
|
||||
<span class="sig-name descname"><span class="pre">--timestamp</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-timestamp" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Add time stamp to verbose output</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-dry-run">
|
||||
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-dry-run" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Run in dry-run mode (don’t actually update files), e.g. for use with –update-signatures.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-theme">
|
||||
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre"><THEME></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-theme" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Specify the color theme to use for –verbose output. Valid themes are ‘dark’, ‘light’, ‘mono’, and ‘plain’. Defaults to ‘dark’ or ‘light’ depending on system dark mode setting.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Options</dt>
|
||||
<dd class="field-odd"><p>dark | light | mono | plain</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-db">
|
||||
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre"><PHOTOS_LIBRARY_PATH></span></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-db" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Specify Photos database path. Path to Photos library/database can be specified using either –db or directly as PHOTOS_LIBRARY positional argument. If neither –db or PHOTOS_LIBRARY provided, will attempt to find the library to use in the following order: 1. last opened library, 2. system library, 3. ~/Pictures/Photos Library.photoslibrary</p>
|
||||
</dd></dl>
|
||||
<p class="rubric">Arguments</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">
|
||||
<span id="cmdoption-osxphotos-exiftool-arg-export-directory"></span><span class="sig-name descname"><span class="pre">EXPORT_DIRECTORY</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Required argument</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-export">
|
||||
<h3>export<a class="headerlink" href="#osxphotos-export" title="Permalink to this headline">#</a></h3>
|
||||
<p>Export photos from the Photos database.
|
||||
@@ -385,6 +517,11 @@ to modify this behavior.</p>
|
||||
<dd><p>Search for photos with keyword KEYWORD. If more than one keyword, treated as “OR”, e.g. find photos matching any keyword</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-no-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--no-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-no-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with no keyword.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-person">
|
||||
<span class="sig-name descname"><span class="pre">--person</span></span><span class="sig-prename descclassname"> <span class="pre"><PERSON></span></span><a class="headerlink" href="#cmdoption-osxphotos-export-person" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with person PERSON. If more than one person, treated as “OR”, e.g. find photos matching any person</p>
|
||||
@@ -767,7 +904,7 @@ to modify this behavior.</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-limit">
|
||||
<span class="sig-name descname"><span class="pre">--limit</span></span><span class="sig-prename descclassname"> <span class="pre"><LIMIT></span></span><a class="headerlink" href="#cmdoption-osxphotos-export-limit" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Export at most LIMIT photos. Useful for testing. Maybe used with –update to export incrementally.</p>
|
||||
<dd><p>Export at most LIMIT photos. Useful for testing. May be used with –update to export incrementally.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-dry-run">
|
||||
@@ -1004,7 +1141,12 @@ to modify this behavior.</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-report">
|
||||
<span class="sig-name descname"><span class="pre">--report</span></span><span class="sig-prename descclassname"> <span class="pre"><REPORT_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-export-report" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Write a CSV formatted report of all files that were exported.</p>
|
||||
<dd><p>Write a report of all files that were exported. The extension of the report filename will be used to determine the format. Valid extensions are: .csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). REPORT_FILE may be a template string (see Templating System), for example, –report ‘export_{today.date}.csv’ will write a CSV report file named with today’s date. See also –append.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-append">
|
||||
<span class="sig-name descname"><span class="pre">--append</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-append" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –report, add data to existing report file instead of overwriting it. See also –report.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-cleanup">
|
||||
@@ -1088,6 +1230,95 @@ to modify this behavior.</p>
|
||||
<dd><p>Required argument</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-exportdb">
|
||||
<h3>exportdb<a class="headerlink" href="#osxphotos-exportdb" title="Permalink to this headline">#</a></h3>
|
||||
<p>Utilities for working with the osxphotos export database</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos exportdb <span class="o">[</span>OPTIONS<span class="o">]</span> EXPORT_DATABASE
|
||||
</pre></div>
|
||||
</div>
|
||||
<p class="rubric">Options</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-version">
|
||||
<span class="sig-name descname"><span class="pre">--version</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-version" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print export database version and exit.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-vacuum">
|
||||
<span class="sig-name descname"><span class="pre">--vacuum</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-vacuum" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Run VACUUM to defragment the database.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-check-signatures">
|
||||
<span class="sig-name descname"><span class="pre">--check-signatures</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-check-signatures" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Check signatures for all exported photos in the database to find signatures that don’t match.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-update-signatures">
|
||||
<span class="sig-name descname"><span class="pre">--update-signatures</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-update-signatures" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Update signatures for all exported photos in the database to match on-disk signatures.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-touch-file">
|
||||
<span class="sig-name descname"><span class="pre">--touch-file</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-touch-file" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Touch files on disk to match created date in Photos library and update export database signatures</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-last-run">
|
||||
<span class="sig-name descname"><span class="pre">--last-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-last-run" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Show last run osxphotos commands used with this database.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-save-config">
|
||||
<span class="sig-name descname"><span class="pre">--save-config</span></span><span class="sig-prename descclassname"> <span class="pre"><CONFIG_FILE></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-save-config" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Save last run configuration to TOML file for use by –load-config.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-info">
|
||||
<span class="sig-name descname"><span class="pre">--info</span></span><span class="sig-prename descclassname"> <span class="pre"><FILE_PATH></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-info" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print information about FILE_PATH contained in the database.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-report">
|
||||
<span class="sig-name descname"><span class="pre">--report</span></span><span class="sig-prename descclassname"> <span class="pre"><REPORT_FILE</span> <span class="pre">RUN_ID></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-report" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Generate an export report as <cite>osxphotos export … –report REPORT_FILE</cite> would have done. This allows you to re-create an export report if you didn’t use the –report option when running <cite>osxphotos export</cite>. The extension of the report file is used to determine the format. Valid extensions are: .csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). RUN_ID may be any integer from -10 to 0 specifying which run to use. For example, <cite>–report report.csv 0</cite> will generate a CSV report for the last run and <cite>–report report.json -1</cite> will generate a JSON report for the second-to-last run (one run prior to last run). REPORT_FILE may be a template string (see Templating System), for example, –report ‘export_{today.date}.csv’ will write a CSV report file named with today’s date. See also –append.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-migrate">
|
||||
<span class="sig-name descname"><span class="pre">--migrate</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-migrate" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Migrate (if needed) export database to current version.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-sql">
|
||||
<span class="sig-name descname"><span class="pre">--sql</span></span><span class="sig-prename descclassname"> <span class="pre"><SQL_STATEMENT></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-sql" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Execute SQL_STATEMENT against export database and print results.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-export-dir">
|
||||
<span class="sig-name descname"><span class="pre">--export-dir</span></span><span class="sig-prename descclassname"> <span class="pre"><export_dir></span></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-export-dir" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional path to export directory (if not parent of export database).</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-append">
|
||||
<span class="sig-name descname"><span class="pre">--append</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-append" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>If used with –report, add data to existing report file instead of overwriting it. See also –report.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-V">
|
||||
<span id="cmdoption-osxphotos-exportdb-v"></span><span id="cmdoption-osxphotos-exportdb-verbose"></span><span class="sig-name descname"><span class="pre">-V</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--verbose</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-V" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Print verbose output.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-dry-run">
|
||||
<span class="sig-name descname"><span class="pre">--dry-run</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-dry-run" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Run in dry-run mode (don’t actually update files), e.g. for use with –update-signatures.</p>
|
||||
</dd></dl>
|
||||
<p class="rubric">Arguments</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-exportdb-arg-EXPORT_DATABASE">
|
||||
<span id="cmdoption-osxphotos-exportdb-arg-export-database"></span><span class="sig-name descname"><span class="pre">EXPORT_DATABASE</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-exportdb-arg-EXPORT_DATABASE" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Required argument</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-help">
|
||||
<h3>help<a class="headerlink" href="#osxphotos-help" title="Permalink to this headline">#</a></h3>
|
||||
<p>Print help; for help on commands: help <command>.</p>
|
||||
@@ -1283,6 +1514,11 @@ if more than one option is provided, they are treated as “AND”
|
||||
<dd><p>Search for photos with keyword KEYWORD. If more than one keyword, treated as “OR”, e.g. find photos matching any keyword</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-no-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--no-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-no-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with no keyword.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-person">
|
||||
<span class="sig-name descname"><span class="pre">--person</span></span><span class="sig-prename descclassname"> <span class="pre"><PERSON></span></span><a class="headerlink" href="#cmdoption-osxphotos-query-person" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with person PERSON. If more than one person, treated as “OR”, e.g. find photos matching any person</p>
|
||||
@@ -1702,6 +1938,11 @@ if more than one option is provided, they are treated as “AND”
|
||||
<dd><p>Search for photos with keyword KEYWORD. If more than one keyword, treated as “OR”, e.g. find photos matching any keyword</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-no-keyword">
|
||||
<span class="sig-name descname"><span class="pre">--no-keyword</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-no-keyword" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with no keyword.</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-person">
|
||||
<span class="sig-name descname"><span class="pre">--person</span></span><span class="sig-prename descclassname"> <span class="pre"><PERSON></span></span><a class="headerlink" href="#cmdoption-osxphotos-repl-person" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Search for photos with person PERSON. If more than one person, treated as “OR”, e.g. find photos matching any person</p>
|
||||
@@ -2089,16 +2330,28 @@ if more than one option is provided, they are treated as “AND”
|
||||
</section>
|
||||
<section id="osxphotos-run">
|
||||
<h3>run<a class="headerlink" href="#osxphotos-run" title="Permalink to this headline">#</a></h3>
|
||||
<p>Run a python file using same environment as osxphotos</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos run <span class="o">[</span>OPTIONS<span class="o">]</span> PYTHON_FILE
|
||||
<p>Run a python file using same environment as osxphotos.
|
||||
Any args are made available to the python file.</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos run <span class="o">[</span>OPTIONS<span class="o">]</span> PYTHON_FILE ARGS
|
||||
</pre></div>
|
||||
</div>
|
||||
<p class="rubric">Options</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-run-h">
|
||||
<span id="cmdoption-osxphotos-run-help"></span><span class="sig-name descname"><span class="pre">-h</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--help</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-run-h" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Show this message and exit</p>
|
||||
</dd></dl>
|
||||
<p class="rubric">Arguments</p>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-run-arg-PYTHON_FILE">
|
||||
<span id="cmdoption-osxphotos-run-arg-python-file"></span><span class="sig-name descname"><span class="pre">PYTHON_FILE</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-run-arg-PYTHON_FILE" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Required argument</p>
|
||||
</dd></dl>
|
||||
<dl class="std option">
|
||||
<dt class="sig sig-object std" id="cmdoption-osxphotos-run-arg-ARGS">
|
||||
<span id="cmdoption-osxphotos-run-arg-args"></span><span class="sig-name descname"><span class="pre">ARGS</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-run-arg-ARGS" title="Permalink to this definition">#</a></dt>
|
||||
<dd><p>Optional argument(s)</p>
|
||||
</dd></dl>
|
||||
</section>
|
||||
<section id="osxphotos-snap">
|
||||
<h3>snap<a class="headerlink" href="#osxphotos-snap" title="Permalink to this headline">#</a></h3>
|
||||
@@ -2447,7 +2700,9 @@ Commands:
|
||||
<li><a class="reference internal" href="#osxphotos-diff">diff</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-docs">docs</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-dump">dump</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-exiftool">exiftool</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-export">export</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-exportdb">exportdb</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-help">help</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-info">info</a></li>
|
||||
<li><a class="reference internal" href="#osxphotos-install">install</a></li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.48.6 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -122,7 +122,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -145,7 +145,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -279,7 +279,20 @@
|
||||
--album-keyword
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-album-keyword">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-album-keyword">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--append
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-append">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-append">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-append">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -291,6 +304,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-burst">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-burst">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--check-signatures
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-check-signatures">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -376,6 +396,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-db">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-db">osxphotos-dump command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-db">osxphotos-export command line option</a>
|
||||
</li>
|
||||
@@ -394,6 +416,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-db">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-snap-db">osxphotos-snap command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--db-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -451,6 +480,8 @@
|
||||
--description-template
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-description-template">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-description-template">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -472,7 +503,11 @@
|
||||
--dry-run
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-dry-run">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-dry-run">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -540,6 +575,8 @@
|
||||
--exiftool-merge-keywords
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-keywords">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-merge-keywords">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -547,6 +584,8 @@
|
||||
--exiftool-merge-persons
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-persons">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-merge-persons">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -554,6 +593,8 @@
|
||||
--exiftool-option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-option">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-option">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -561,6 +602,8 @@
|
||||
--exiftool-path
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-path">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exiftool-path">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-e">osxphotos-timewarp command line option</a>
|
||||
@@ -578,12 +621,21 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-export-by-date">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--export-dir
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-export-dir">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--exportdb
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exportdb">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-exportdb">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -728,6 +780,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-hdr">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-hdr">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--help
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-h">osxphotos-run command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -756,6 +815,8 @@
|
||||
--ignore-date-modified
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-ignore-date-modified">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-ignore-date-modified">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -784,6 +845,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-incloud">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-incloud">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--info
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-info">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -858,6 +926,8 @@
|
||||
--keyword-template
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-keyword-template">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-keyword-template">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -870,6 +940,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-label">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-label">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--last-run
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-last-run">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -908,6 +985,8 @@
|
||||
--load-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-load-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-load-config">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -938,6 +1017,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-max-size">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-max-size">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--migrate
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -993,6 +1079,17 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-description">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-description">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--no-keyword
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-keyword">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-keyword">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-keyword">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1046,6 +1143,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-title">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--not-burst
|
||||
|
||||
@@ -1066,8 +1165,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-not-cloudasset">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
--not-favorite
|
||||
|
||||
@@ -1294,6 +1391,8 @@
|
||||
--person-keyword
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-person-keyword">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-person-keyword">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -1428,6 +1527,8 @@
|
||||
--replace-keywords
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -1435,7 +1536,11 @@
|
||||
--report
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-report">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-report">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-report">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1456,7 +1561,11 @@
|
||||
--save-config
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-save-config">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-save-config">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-save-config">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1575,6 +1684,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-slow-mo">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-slow-mo">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--sql
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-sql">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1595,6 +1711,8 @@
|
||||
--theme
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-theme">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-theme">osxphotos-timewarp command line option</a>
|
||||
@@ -1629,6 +1747,8 @@
|
||||
--timestamp
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-timestamp">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-timestamp">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-timestamp">osxphotos-timewarp command line option</a>
|
||||
@@ -1686,6 +1806,8 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-touch-file">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-touch-file">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1693,6 +1815,13 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-update">osxphotos-export command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--update-signatures
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-update-signatures">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1754,6 +1883,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-uuid-from-file">osxphotos-query command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-uuid-from-file">osxphotos-repl command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
--vacuum
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-vacuum">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1761,8 +1897,12 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
|
||||
</li>
|
||||
@@ -1772,6 +1912,8 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-v">osxphotos command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-version">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1848,6 +1990,13 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-0">osxphotos-timewarp command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-uuid-f">osxphotos-uuid command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
-h
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-h">osxphotos-run command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -1938,8 +2087,12 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-diff-V">osxphotos-diff command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-V">osxphotos-export command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
|
||||
</li>
|
||||
@@ -1999,14 +2152,14 @@
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.AlbumInfo">AlbumInfo (class in osxphotos)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.albums">albums (osxphotos.PhotoInfo property)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums">(osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_as_dict">albums_as_dict (osxphotos.PhotosDB property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.albums_shared">albums_shared (osxphotos.PhotosDB property)</a>
|
||||
@@ -2019,6 +2172,13 @@
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportResults.all_files">all_files() (osxphotos.ExportResults method)</a>
|
||||
</li>
|
||||
<li>
|
||||
ARGS
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-arg-ARGS">osxphotos-run command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.ExifTool.asdict">asdict() (osxphotos.ExifTool method)</a>
|
||||
|
||||
<ul>
|
||||
@@ -2193,9 +2353,9 @@
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.exiftool">(osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions.exiftool_flags">exiftool_flags (osxphotos.ExportOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.exiftool_json_sidecar">exiftool_json_sidecar() (osxphotos.PhotoExporter method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.export">export() (osxphotos.PhotoExporter method)</a>
|
||||
|
||||
@@ -2203,12 +2363,28 @@
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.export">(osxphotos.PhotoInfo method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions.export_as_hardlink">export_as_hardlink (osxphotos.ExportOptions attribute)</a>
|
||||
</li>
|
||||
<li>
|
||||
EXPORT_DATABASE
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-arg-EXPORT_DATABASE">osxphotos-exportdb command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions.export_db">export_db (osxphotos.ExportOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.export_dir">export_dir (osxphotos.ExportDB property)</a>
|
||||
</li>
|
||||
<li>
|
||||
EXPORT_DIRECTORY
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">osxphotos-exiftool command line option</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB">ExportDB (class in osxphotos)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportOptions">ExportOptions (class in osxphotos)</a>
|
||||
@@ -2282,6 +2458,10 @@
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotosDB.get_db_connection">get_db_connection() (osxphotos.PhotosDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_export_results">get_export_results() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_exported_files">get_exported_files() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_file_record">get_file_record() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
@@ -2292,11 +2472,11 @@
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_photo_video_type">get_photo_video_type() (osxphotos.PhotoTemplate method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_photoinfo_for_uuid">get_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_previous_uuids">get_previous_uuids() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.get_previous_uuids">get_previous_uuids() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_template_value">get_template_value() (osxphotos.PhotoTemplate method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoTemplate.get_template_value_exiftool">get_template_value_exiftool() (osxphotos.PhotoTemplate method)</a>
|
||||
@@ -2561,6 +2741,8 @@
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.no_comment">no_comment (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.no_description">no_description (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.no_keyword">no_keyword (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.no_likes">no_likes (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
@@ -2575,11 +2757,11 @@
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_cloudasset">not_cloudasset (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_favorite">not_favorite (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_hidden">not_hidden (osxphotos.QueryOptions attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.QueryOptions.not_in_album">not_in_album (osxphotos.QueryOptions attribute)</a>
|
||||
@@ -2689,6 +2871,57 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-json">--json</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-dump-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
osxphotos-exiftool command line option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-album-keyword">--album-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-append">--append</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db">--db</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-db-config">--db-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-description-template">--description-template</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-dry-run">--dry-run</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-keywords">--exiftool-merge-keywords</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-merge-persons">--exiftool-merge-persons</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-option">--exiftool-option</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exiftool-path">--exiftool-path</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-exportdb">--exportdb</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-ignore-date-modified">--ignore-date-modified</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-keyword-template">--keyword-template</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-load-config">--load-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-person-keyword">--person-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-replace-keywords">--replace-keywords</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-report">--report</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-save-config">--save-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-theme">--theme</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-timestamp">--timestamp</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">--verbose</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-V">-V</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exiftool-arg-EXPORT_DIRECTORY">EXPORT_DIRECTORY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -2710,6 +2943,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-album">--album</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-album-keyword">--album-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-append">--append</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-burst">--burst</a>
|
||||
</li>
|
||||
@@ -2828,6 +3063,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-comment">--no-comment</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-description">--no-description</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-keyword">--no-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-no-likes">--no-likes</a>
|
||||
</li>
|
||||
@@ -2982,6 +3219,45 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-arg-DEST">DEST</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-export-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
osxphotos-exportdb command line option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-append">--append</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-check-signatures">--check-signatures</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-dry-run">--dry-run</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-export-dir">--export-dir</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-info">--info</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-last-run">--last-run</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-migrate">--migrate</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-report">--report</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-save-config">--save-config</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-sql">--sql</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-touch-file">--touch-file</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-update-signatures">--update-signatures</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-vacuum">--vacuum</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">--verbose</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-version">--version</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-V">-V</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-exportdb-arg-EXPORT_DATABASE">EXPORT_DATABASE</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li>
|
||||
@@ -3044,6 +3320,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-list-json">--json</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
osxphotos-persons command line option
|
||||
|
||||
@@ -3147,6 +3425,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-comment">--no-comment</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-description">--no-description</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-keyword">--no-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-no-likes">--no-likes</a>
|
||||
</li>
|
||||
@@ -3237,8 +3517,6 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
osxphotos-repl command line option
|
||||
|
||||
@@ -3318,6 +3596,8 @@
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-comment">--no-comment</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-description">--no-description</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-keyword">--no-keyword</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-repl-no-likes">--no-likes</a>
|
||||
</li>
|
||||
@@ -3410,6 +3690,12 @@
|
||||
osxphotos-run command line option
|
||||
|
||||
<ul>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-h">--help</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-h">-h</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-arg-ARGS">ARGS</a>
|
||||
</li>
|
||||
<li><a href="cli.html#cmdoption-osxphotos-run-arg-PYTHON_FILE">PYTHON_FILE</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
@@ -3798,6 +4084,8 @@
|
||||
<li><a href="reference.html#osxphotos.ExportDB.set_config">set_config() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.set_debug">set_debug() (in module osxphotos)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.set_export_results">set_export_results() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.ExportDB.set_photoinfo_for_uuid">set_photoinfo_for_uuid() (osxphotos.ExportDB method)</a>
|
||||
</li>
|
||||
@@ -3973,6 +4261,8 @@
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#osxphotos.PhotoInfo.width">width (osxphotos.PhotoInfo property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#osxphotos.PhotoExporter.write_exiftool_metadata_to_file">write_exiftool_metadata_to_file() (osxphotos.PhotoExporter method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>osxphotos 0.48.6 documentation</title>
|
||||
<title>osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="#"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="#"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -237,7 +237,9 @@
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-diff">diff</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-docs">docs</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-dump">dump</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-exiftool">exiftool</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-export">export</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-exportdb">exportdb</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-help">help</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-info">info</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-install">install</a></li>
|
||||
|
||||
BIN
docs/objects.inv
BIN
docs/objects.inv
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotos’s documentation!" href="index.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos - osxphotos 0.48.6 documentation</title>
|
||||
<title>OSXPhotos - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos python API" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.48.6 documentation</title>
|
||||
<title>OSXPhotos Python Package Overview - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.48.6 documentation</title>
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -122,7 +122,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -145,7 +145,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.48.6 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.49.2 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||
@@ -121,7 +121,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -144,7 +144,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Template System - osxphotos 0.48.6 documentation</title>
|
||||
<title>OSXPhotos Template System - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -567,7 +567,7 @@
|
||||
<td><p>a carriage return + line feed: ‘rn’</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.48.6’</p></td>
|
||||
<td><p>The osxphotos version, e.g. ‘0.49.2’</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
|
||||
<td><p>The full command line used to run osxphotos</p></td>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
|
||||
|
||||
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.48.6 documentation</title>
|
||||
<title>OSXPhotos Tutorial - osxphotos 0.49.2 documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=68f4518137b9aefe99b631505a2064c3c42c9852" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||
@@ -124,7 +124,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="header-center">
|
||||
<a href="index.html"><div class="brand">osxphotos 0.48.6 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">osxphotos 0.49.2 documentation</div></a>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="theme-toggle-container theme-toggle-header">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||
|
||||
|
||||
<span class="sidebar-brand-text">osxphotos 0.48.6 documentation</span>
|
||||
<span class="sidebar-brand-text">osxphotos 0.49.2 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||
@@ -314,7 +314,7 @@
|
||||
<h2>Exporting metadata with exported photos<a class="headerlink" href="#exporting-metadata-with-exported-photos" title="Permalink to this headline">#</a></h2>
|
||||
<p>Photos tracks a tremendous amount of metadata associated with photos in the library such as keywords, faces and persons, reverse geolocation data, and image classification labels. Photos’ native export capability does not preserve most of this metadata. osxphotos can, however, access and preserve almost all the metadata associated with photos. Using the free <cite>``exiftool`</cite> <<a class="reference external" href="https://exiftool.org/">https://exiftool.org/</a>>`_ app, osxphotos can write metadata to exported photos. Follow the instructions on the exiftool website to install exiftool then you can use the <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> option to write metadata to exported photos:</p>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">osxphotos</span> <span class="pre">export</span> <span class="pre">/path/to/export</span> <span class="pre">--exiftool</span></code></p>
|
||||
<p>This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> to modify the metadata that is written by <code class="docutils literal notranslate"><span class="pre">exiftool</span></code>. For example, you can use the <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code> option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchal keywords in the format used by Lightroom Classic:</p>
|
||||
<p>This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> to modify the metadata that is written by <code class="docutils literal notranslate"><span class="pre">exiftool</span></code>. For example, you can use the <code class="docutils literal notranslate"><span class="pre">--keyword-template</span></code> option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>osxphotos export /path/to/export --exiftool --keyword-template "{folder_album(>)}"
|
||||
│ │
|
||||
│ │
|
||||
@@ -325,7 +325,7 @@
|
||||
for joining the folders and albums. For example,
|
||||
if photo is in Folder1/Folder2/Album, (>) produces
|
||||
"Folder1>Folder2>Album" which some programs, such as
|
||||
Lightroom Classic, treat as hierarchal keywords
|
||||
Lightroom Classic, treat as hierarchical keywords
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above command will write all the regular metadata that <code class="docutils literal notranslate"><span class="pre">--exiftool</span></code> normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form “Folder1>Folder2>Album”. If you did not include the <code class="docutils literal notranslate"><span class="pre">(>)</span></code> in the template string (e.g. <code class="docutils literal notranslate"><span class="pre">{folder_album}</span></code>), folder_album would render in form “Folder1/Folder2/Album”.</p>
|
||||
|
||||
@@ -320,7 +320,7 @@ Template Substitutions
|
||||
* - {crlf}
|
||||
- a carriage return + line feed: '\r\n'
|
||||
* - {osxphotos_version}
|
||||
- The osxphotos version, e.g. '0.48.6'
|
||||
- The osxphotos version, e.g. '0.49.2'
|
||||
* - {osxphotos_cmd_line}
|
||||
- The full command line used to run osxphotos
|
||||
* - {album}
|
||||
|
||||
@@ -195,7 +195,7 @@ Photos tracks a tremendous amount of metadata associated with photos in the libr
|
||||
|
||||
``osxphotos export /path/to/export --exiftool``
|
||||
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with ``--exiftool`` to modify the metadata that is written by ``exiftool``. For example, you can use the ``--keyword-template`` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchal keywords in the format used by Lightroom Classic:
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with ``--exiftool`` to modify the metadata that is written by ``exiftool``. For example, you can use the ``--keyword-template`` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:
|
||||
|
||||
.. code-block::
|
||||
|
||||
@@ -209,7 +209,7 @@ This will write basic metadata such as keywords, persons, and GPS location to th
|
||||
for joining the folders and albums. For example,
|
||||
if photo is in Folder1/Folder2/Album, (>) produces
|
||||
"Folder1>Folder2>Album" which some programs, such as
|
||||
Lightroom Classic, treat as hierarchal keywords
|
||||
Lightroom Classic, treat as hierarchical keywords
|
||||
|
||||
|
||||
The above command will write all the regular metadata that ``--exiftool`` normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form "Folder1>Folder2>Album". If you did not include the ``(>)`` in the template string (e.g. ``{folder_album}``\ ), folder_album would render in form "Folder1/Folder2/Album".
|
||||
|
||||
22
examples/detect_text_in_photos.py
Normal file
22
examples/detect_text_in_photos.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Use osxphotos and photoscript to find text in photos and update the photo description with detected text"""
|
||||
|
||||
import photoscript
|
||||
|
||||
import osxphotos
|
||||
|
||||
if __name__ == "__main__":
|
||||
# get photos selected in Photos
|
||||
selection = photoscript.PhotosLibrary().selection
|
||||
photosdb = osxphotos.PhotosDB()
|
||||
photos = photosdb.photos(uuid=[s.uuid for s in selection])
|
||||
for photo in photos:
|
||||
detected_text = photo.detected_text()
|
||||
if not detected_text:
|
||||
continue
|
||||
# detected text is tuple of (text, confidence)
|
||||
for text, confidence in detected_text:
|
||||
description = photo.description or ""
|
||||
# set confidence level to whatever you like
|
||||
if confidence > 0.8 and text not in description:
|
||||
print(f"Adding {text} to {photo.original_filename} ({photo.uuid})")
|
||||
photoscript.Photo(photo.uuid).description += f" {text}"
|
||||
90
examples/photo_inspect.py
Normal file
90
examples/photo_inspect.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Print information about one or more items selected in Photos; run with `osxphotos run photo_inspect.py`"""
|
||||
|
||||
from time import sleep
|
||||
|
||||
import bitmath
|
||||
from photoscript import PhotosLibrary
|
||||
from rich import print
|
||||
|
||||
from osxphotos import PhotoInfo, PhotosDB
|
||||
from osxphotos.utils import dd_to_dms_str
|
||||
|
||||
|
||||
def get_photo_type(photo: PhotoInfo):
|
||||
"""Return a string describing the type of photo"""
|
||||
if photo.ismovie:
|
||||
photo_type = "video"
|
||||
else:
|
||||
raw = "RAW+JPEG " if photo.has_raw else "RAW " if photo.israw else ""
|
||||
photo_type = f"{raw}photo"
|
||||
if photo.burst:
|
||||
photo_type += " burst"
|
||||
if photo.live_photo:
|
||||
photo_type += " live"
|
||||
if photo.selfie:
|
||||
photo_type += " selfie"
|
||||
if photo.panorama:
|
||||
photo_type += " panorama"
|
||||
if photo.hdr:
|
||||
photo_type += " HDR"
|
||||
if photo.screenshot:
|
||||
photo_type += " screenshot"
|
||||
if photo.slow_mo:
|
||||
photo_type += " slow-mo"
|
||||
if photo.time_lapse:
|
||||
photo_type += " time-lapse"
|
||||
if photo.portrait:
|
||||
photo_type += " portrait"
|
||||
return photo_type
|
||||
|
||||
|
||||
def inspect_photo(photo: PhotoInfo):
|
||||
"""Print info about an osxphotos PhotoInfo object"""
|
||||
|
||||
properties = [
|
||||
f"filename: {photo.original_filename}",
|
||||
f"type: {get_photo_type(photo)}",
|
||||
f"uuid: {photo.uuid}",
|
||||
f"date: {photo.date.isoformat()}",
|
||||
f"dimensions: {photo.height} x {photo.width}",
|
||||
f"file size: {bitmath.Byte(photo.original_filesize).to_MB()}",
|
||||
f"title: {photo.title or '-'}",
|
||||
f"description: {photo.description or '-'}",
|
||||
f"edited: {'✔' if photo.hasadjustments else '-'}",
|
||||
f"keywords: {', '.join(photo.keywords) or '-'}",
|
||||
f"persons: {', '.join(photo.persons) or '-'}",
|
||||
f"location: {', '.join(dd_to_dms_str(*photo.location)) if photo.location[0] else '-'}",
|
||||
f"place: {photo.place.name if photo.place else '-'}",
|
||||
f"categories: {', '.join(photo.labels) or '-'}",
|
||||
f"albums: {', '.join(photo.albums) or '-'}",
|
||||
f"favorite: {'♥' if photo.favorite else '-'}",
|
||||
]
|
||||
if photo.exif_info:
|
||||
properties.extend(
|
||||
[
|
||||
f"camera: {photo.exif_info.camera_make or '-'} {photo.exif_info.camera_model or '-'}",
|
||||
f"lens: {photo.exif_info.lens_model or '-'}",
|
||||
]
|
||||
)
|
||||
for property in properties:
|
||||
print(property)
|
||||
print("-" * 20)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Loading Photos Library...")
|
||||
photosdb = PhotosDB()
|
||||
photoslib = PhotosLibrary()
|
||||
|
||||
# keep track of last seen UUIDs so we don't print duplicates
|
||||
last_uuids = []
|
||||
print("Select one or more photos in Photos (press Ctrl+C to quit)")
|
||||
while True:
|
||||
if photos := photoslib.selection:
|
||||
uuids = sorted([photo.uuid for photo in photos])
|
||||
if uuids != last_uuids:
|
||||
for photo in photos:
|
||||
photoinfo = photosdb.get_photo(photo.uuid)
|
||||
inspect_photo(photoinfo)
|
||||
last_uuids = uuids
|
||||
sleep(0.200)
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.48.6"
|
||||
__version__ = "0.49.2"
|
||||
|
||||
@@ -48,6 +48,7 @@ from .cli import cli_main
|
||||
from .common import get_photos_db, load_uuid_from_file
|
||||
from .debug_dump import debug_dump
|
||||
from .dump import dump
|
||||
from .exiftool_cli import exiftool
|
||||
from .export import export
|
||||
from .exportdb import exportdb
|
||||
from .grep import grep
|
||||
@@ -74,6 +75,7 @@ __all__ = [
|
||||
"debug_dump",
|
||||
"diff",
|
||||
"dump",
|
||||
"exiftool_cli",
|
||||
"export",
|
||||
"exportdb",
|
||||
"grep",
|
||||
|
||||
@@ -258,6 +258,20 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
ISC_LICENSE = """
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
"""
|
||||
|
||||
LICENSE = dedent(
|
||||
f"""
|
||||
osxphotos is copyright (c) 2019-2022 by Rhet Turnbull and is licensed under the MIT license:
|
||||
@@ -273,6 +287,12 @@ osxphotos uses the following 3rd party software licensed under the Apache 2.0 Li
|
||||
tenacity (Copyright Julien Danjou)
|
||||
|
||||
{APACHE_2_0_LICENSE}
|
||||
|
||||
osxphotos uses the following 3rd part software licensed under the ISC License:
|
||||
|
||||
xdg (Copyright 2016-2021 Scott Stevenson <scott@stevenson.io>)
|
||||
|
||||
{ISC_LICENSE}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from .common import DB_OPTION, JSON_OPTION, OSXPHOTOS_HIDDEN
|
||||
from .debug_dump import debug_dump
|
||||
from .docs import docs
|
||||
from .dump import dump
|
||||
from .exiftool_cli import exiftool
|
||||
from .export import export
|
||||
from .exportdb import exportdb
|
||||
from .grep import grep
|
||||
@@ -67,6 +68,7 @@ for command in [
|
||||
diff,
|
||||
docs,
|
||||
dump,
|
||||
exiftool,
|
||||
export,
|
||||
exportdb,
|
||||
grep,
|
||||
|
||||
@@ -7,6 +7,7 @@ from datetime import datetime
|
||||
|
||||
import click
|
||||
from packaging import version
|
||||
from xdg import xdg_config_home, xdg_data_home
|
||||
|
||||
import osxphotos
|
||||
from osxphotos._constants import APP_NAME
|
||||
@@ -582,12 +583,20 @@ def load_uuid_from_file(filename):
|
||||
|
||||
def get_config_dir() -> pathlib.Path:
|
||||
"""Get the directory where config files are stored; create it if necessary."""
|
||||
config_dir = pathlib.Path.home() / ".config" / APP_NAME
|
||||
config_dir = xdg_config_home() / APP_NAME
|
||||
if not config_dir.is_dir():
|
||||
config_dir.mkdir(parents=True)
|
||||
return config_dir
|
||||
|
||||
|
||||
def get_data_dir() -> pathlib.Path:
|
||||
"""Get the director where local user data files are stored; create it if necessary"""
|
||||
data_dir = xdg_data_home() / APP_NAME
|
||||
if not data_dir.is_dir():
|
||||
data_dir.mkdir(parents=True)
|
||||
return data_dir
|
||||
|
||||
|
||||
def check_version():
|
||||
"""Check for updates"""
|
||||
latest_version, _ = get_latest_version()
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import pathlib
|
||||
import shutil
|
||||
import zipfile
|
||||
from contextlib import suppress
|
||||
from typing import Optional
|
||||
|
||||
import click
|
||||
|
||||
from osxphotos._version import __version__
|
||||
|
||||
from .common import get_config_dir
|
||||
from .common import get_config_dir, get_data_dir
|
||||
|
||||
|
||||
@click.command()
|
||||
@@ -18,7 +19,14 @@ from .common import get_config_dir
|
||||
def docs(ctx, cli_obj):
|
||||
"""Open osxphotos documentation in your browser."""
|
||||
|
||||
docs_dir = get_config_dir() / "docs"
|
||||
# first check if docs installed in old location in confir dir and if so, delete them
|
||||
old_docs_dir = get_config_dir() / "docs"
|
||||
if old_docs_dir.exists():
|
||||
with suppress(Exception):
|
||||
shutil.rmtree(str(old_docs_dir))
|
||||
|
||||
# now check if docs installed in correct location in data dir and if not, copy them
|
||||
docs_dir = get_data_dir() / "docs"
|
||||
docs_version = get_docs_version(docs_dir)
|
||||
if not docs_version or docs_version != __version__:
|
||||
click.echo(f"Copying docs for osxphotos version {__version__}")
|
||||
@@ -50,10 +58,10 @@ def copy_docs():
|
||||
# docs are in osxphotos/docs and this file is in osxphotos/cli
|
||||
src_dir = pathlib.Path(__file__).parent.parent / "docs"
|
||||
docs_zip = src_dir / "docs.zip"
|
||||
config_dir = get_config_dir()
|
||||
data_dir = get_data_dir()
|
||||
with zipfile.ZipFile(str(docs_zip), "r") as zf:
|
||||
zf.extractall(path=str(config_dir))
|
||||
set_docs_version(config_dir / "docs", __version__)
|
||||
zf.extractall(path=str(data_dir))
|
||||
set_docs_version(data_dir / "docs", __version__)
|
||||
|
||||
|
||||
def set_docs_version(docs_dir: pathlib.Path, version: str):
|
||||
|
||||
408
osxphotos/cli/exiftool_cli.py
Normal file
408
osxphotos/cli/exiftool_cli.py
Normal file
@@ -0,0 +1,408 @@
|
||||
"""exiftool command for osxphotos CLI to update an previous export with exiftool metadata"""
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Callable
|
||||
|
||||
import click
|
||||
|
||||
from osxphotos import PhotosDB
|
||||
from osxphotos._constants import OSXPHOTOS_EXPORT_DB
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.configoptions import ConfigOptions, ConfigOptionsLoadError
|
||||
from osxphotos.export_db import ExportDB, ExportDBInMemory
|
||||
from osxphotos.export_db_utils import export_db_get_config
|
||||
from osxphotos.fileutil import FileUtil, FileUtilNoOp
|
||||
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
|
||||
from osxphotos.utils import pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
rich_click_echo,
|
||||
rich_echo,
|
||||
rich_echo_error,
|
||||
set_rich_console,
|
||||
set_rich_theme,
|
||||
set_rich_timestamp,
|
||||
)
|
||||
from .color_themes import get_theme
|
||||
from .common import DB_OPTION, THEME_OPTION, get_photos_db
|
||||
from .export import export, render_and_validate_report
|
||||
from .param_types import ExportDBType, TemplateString
|
||||
from .report_writer import ReportWriterNoOp, report_writer_factory
|
||||
from .rich_progress import rich_progress
|
||||
from .verbose import get_verbose_console, verbose_print
|
||||
|
||||
|
||||
@click.command(name="exiftool")
|
||||
@click.option(
|
||||
"--db-config",
|
||||
is_flag=True,
|
||||
help="Load configuration options from the export database to match the last export; "
|
||||
"If any other command line options are used in conjunction with --db-config, "
|
||||
"they will override the corresponding values loaded from the export database; "
|
||||
"see also --load-config.",
|
||||
)
|
||||
@click.option(
|
||||
"--load-config",
|
||||
required=False,
|
||||
metavar="CONFIG_FILE",
|
||||
default=None,
|
||||
help=(
|
||||
"Load options from file as written with --save-config. "
|
||||
"If any other command line options are used in conjunction with --load-config, "
|
||||
"they will override the corresponding values in the config file; "
|
||||
"see also --db-config."
|
||||
),
|
||||
type=click.Path(exists=True),
|
||||
)
|
||||
@click.option(
|
||||
"--save-config",
|
||||
required=False,
|
||||
metavar="CONFIG_FILE",
|
||||
default=None,
|
||||
help="Save options to file for use with --load-config. File format is TOML. ",
|
||||
type=click.Path(),
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-path",
|
||||
metavar="EXIFTOOL_PATH",
|
||||
type=click.Path(exists=True),
|
||||
help="Optionally specify path to exiftool; if not provided, will look for exiftool in $PATH.",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-option",
|
||||
multiple=True,
|
||||
metavar="OPTION",
|
||||
help="Optional flag/option to pass to exiftool when using --exiftool. "
|
||||
"For example, --exiftool-option '-m' to ignore minor warnings. "
|
||||
"Specify these as you would on the exiftool command line. "
|
||||
"See exiftool docs at https://exiftool.org/exiftool_pod.html for full list of options. "
|
||||
"More than one option may be specified by repeating the option, e.g. "
|
||||
"--exiftool-option '-m' --exiftool-option '-F'. ",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-merge-keywords",
|
||||
is_flag=True,
|
||||
help="Merge any keywords found in the original file with keywords used for '--exiftool' and '--sidecar'.",
|
||||
)
|
||||
@click.option(
|
||||
"--exiftool-merge-persons",
|
||||
is_flag=True,
|
||||
help="Merge any persons found in the original file with persons used for '--exiftool' and '--sidecar'.",
|
||||
)
|
||||
@click.option(
|
||||
"--ignore-date-modified",
|
||||
is_flag=True,
|
||||
help="If used with --exiftool or --sidecar, will ignore the photo "
|
||||
"modification date and set EXIF:ModifyDate to EXIF:DateTimeOriginal; "
|
||||
"this is consistent with how Photos handles the EXIF:ModifyDate tag.",
|
||||
)
|
||||
@click.option(
|
||||
"--person-keyword",
|
||||
is_flag=True,
|
||||
help="Use person in image as keyword/tag when exporting metadata.",
|
||||
)
|
||||
@click.option(
|
||||
"--album-keyword",
|
||||
is_flag=True,
|
||||
help="Use album name as keyword/tag when exporting metadata.",
|
||||
)
|
||||
@click.option(
|
||||
"--keyword-template",
|
||||
metavar="TEMPLATE",
|
||||
multiple=True,
|
||||
default=None,
|
||||
help="For use with --exiftool, --sidecar; specify a template string to use as "
|
||||
"keyword in the form '{name,DEFAULT}' "
|
||||
"This is the same format as --directory. For example, if you wanted to add "
|
||||
"the full path to the folder and album photo is contained in as a keyword when exporting "
|
||||
'you could specify --keyword-template "{folder_album}" '
|
||||
'You may specify more than one template, for example --keyword-template "{folder_album}" '
|
||||
'--keyword-template "{created.year}". '
|
||||
"See '--replace-keywords' and Templating System below.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--replace-keywords",
|
||||
is_flag=True,
|
||||
help="Replace keywords with any values specified with --keyword-template. "
|
||||
"By default, --keyword-template will add keywords to any keywords already associated "
|
||||
"with the photo. If --replace-keywords is specified, values from --keyword-template "
|
||||
"will replace any existing keywords instead of adding additional keywords.",
|
||||
)
|
||||
@click.option(
|
||||
"--description-template",
|
||||
metavar="TEMPLATE",
|
||||
multiple=False,
|
||||
default=None,
|
||||
help="For use with --exiftool, --sidecar; specify a template string to use as "
|
||||
"description in the form '{name,DEFAULT}' "
|
||||
"This is the same format as --directory. For example, if you wanted to append "
|
||||
"'exported with osxphotos on [today's date]' to the description, you could specify "
|
||||
'--description-template "{descr} exported with osxphotos on {today.date}" '
|
||||
"See Templating System below.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--exportdb",
|
||||
help="Optional path to export database (if not in the default location in the export directory).",
|
||||
type=ExportDBType(),
|
||||
)
|
||||
@click.option(
|
||||
"--report",
|
||||
metavar="REPORT_FILE",
|
||||
help="Write a report of all files that were exported. "
|
||||
"The extension of the report filename will be used to determine the format. "
|
||||
"Valid extensions are: "
|
||||
".csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). "
|
||||
"REPORT_FILE may be a template string (see Templating System), for example, "
|
||||
"--report 'export_{today.date}.csv' will write a CSV report file named with today's date. "
|
||||
"See also --append.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--append",
|
||||
is_flag=True,
|
||||
help="If used with --report, add data to existing report file instead of overwriting it. "
|
||||
"See also --report.",
|
||||
)
|
||||
@click.option("--verbose", "-V", is_flag=True, help="Print verbose output.")
|
||||
@click.option("--timestamp", is_flag=True, help="Add time stamp to verbose output")
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
is_flag=True,
|
||||
help="Run in dry-run mode (don't actually update files), e.g. for use with --update-signatures.",
|
||||
)
|
||||
@THEME_OPTION
|
||||
@DB_OPTION
|
||||
@click.argument(
|
||||
"export_dir",
|
||||
metavar="EXPORT_DIRECTORY",
|
||||
nargs=1,
|
||||
type=click.Path(exists=True, file_okay=False),
|
||||
)
|
||||
def exiftool(
|
||||
album_keyword,
|
||||
append,
|
||||
db_config,
|
||||
db,
|
||||
description_template,
|
||||
dry_run,
|
||||
exiftool_merge_keywords,
|
||||
exiftool_merge_persons,
|
||||
exiftool_option,
|
||||
exiftool_path,
|
||||
export_dir,
|
||||
exportdb,
|
||||
ignore_date_modified,
|
||||
keyword_template,
|
||||
load_config,
|
||||
person_keyword,
|
||||
replace_keywords,
|
||||
report,
|
||||
save_config,
|
||||
theme,
|
||||
timestamp,
|
||||
verbose,
|
||||
):
|
||||
"""Run exiftool on previously exported files to update metadata.
|
||||
|
||||
If you previously exported photos with `osxphotos export` but did not include the
|
||||
`--exiftool` option and you now want to update the metadata of the exported files with
|
||||
exiftool, you can use this command to do so.
|
||||
|
||||
If you simply re-run the `osxphotos export` with `--update` and `--exiftool`, osxphotos will
|
||||
re-export all photos because it will detect that the previously exported photos do not have the
|
||||
exiftool metadata updates. This command will run exiftool on the previously exported photos
|
||||
to update all metadata then will update the export database so that using `--exiftool --update`
|
||||
with `osxphotos export` in the future will work correctly and not unnecessarily re-export photos.
|
||||
"""
|
||||
# save locals for initializing config options
|
||||
locals_ = locals()
|
||||
|
||||
if load_config and db_config:
|
||||
raise click.UsageError("Cannot specify both --load-config and --db-config")
|
||||
|
||||
exportdb = exportdb or pathlib.Path(export_dir) / OSXPHOTOS_EXPORT_DB
|
||||
if not exportdb.exists():
|
||||
raise click.UsageError(f"Export database {exportdb} does not exist")
|
||||
|
||||
# grab all the variables we need from the export command
|
||||
# export is a click Command so can walk through it's params to get the option names
|
||||
for param in export.params:
|
||||
if param.name not in locals_:
|
||||
locals_[param.name] = None
|
||||
|
||||
# need to ensure --exiftool is true in the config options
|
||||
locals_["exiftool"] = True
|
||||
config = ConfigOptions(
|
||||
"export",
|
||||
locals_,
|
||||
ignore=[
|
||||
"cli_obj",
|
||||
"config_only",
|
||||
"ctx",
|
||||
"db_config",
|
||||
"dest",
|
||||
"export_dir",
|
||||
"load_config",
|
||||
"save_config",
|
||||
],
|
||||
)
|
||||
color_theme = get_theme(theme)
|
||||
verbose_ = verbose_print(
|
||||
verbose, timestamp, rich=True, theme=color_theme, highlight=False
|
||||
)
|
||||
# set console for rich_echo to be same as for verbose_
|
||||
set_rich_console(get_verbose_console())
|
||||
set_rich_theme(color_theme)
|
||||
set_rich_timestamp(timestamp)
|
||||
|
||||
# load config options from either file or export database
|
||||
# values already set in config will take precedence over any values
|
||||
# in the config file or database
|
||||
if load_config:
|
||||
try:
|
||||
config.load_from_file(load_config)
|
||||
except ConfigOptionsLoadError as e:
|
||||
rich_click_echo(
|
||||
f"[error]Error parsing {load_config} config file: {e.message}", err=True
|
||||
)
|
||||
sys.exit(1)
|
||||
verbose_(f"Loaded options from file [filepath]{load_config}")
|
||||
elif db_config:
|
||||
config = export_db_get_config(exportdb, config)
|
||||
verbose_("Loaded options from export database")
|
||||
|
||||
# from here on out, use config.param_name instead of using the params passed into the function
|
||||
# as the values may have been updated from config file or database
|
||||
if load_config or db_config:
|
||||
# config file might have changed verbose
|
||||
color_theme = get_theme(config.theme)
|
||||
verbose_ = verbose_print(
|
||||
config.verbose,
|
||||
config.timestamp,
|
||||
rich=True,
|
||||
theme=color_theme,
|
||||
highlight=False,
|
||||
)
|
||||
# set console for rich_echo to be same as for verbose_
|
||||
set_rich_console(get_verbose_console())
|
||||
set_rich_timestamp(config.timestamp)
|
||||
|
||||
# validate options
|
||||
if append and not report:
|
||||
raise click.UsageError("--append requires --report")
|
||||
|
||||
# need to ensure we have a photos database
|
||||
config.db = get_photos_db(config.db)
|
||||
|
||||
if save_config:
|
||||
verbose_(f"Saving options to config file '[filepath]{save_config}'")
|
||||
config.write_to_file(save_config)
|
||||
|
||||
process_files(exportdb, export_dir, verbose=verbose_, options=config)
|
||||
|
||||
|
||||
def process_files(
|
||||
exportdb: str, export_dir: str, verbose: Callable, options: ConfigOptions
|
||||
):
|
||||
"""Process files in the export database.
|
||||
|
||||
Args:
|
||||
exportdb: Path to export database.
|
||||
export_dir: Path to export directory.
|
||||
verbose: Callable for verbose output.
|
||||
options: ConfigOptions
|
||||
"""
|
||||
|
||||
if options.report:
|
||||
report = render_and_validate_report(
|
||||
options.report, options.exiftool_path, export_dir
|
||||
)
|
||||
report_writer = report_writer_factory(report, options.append)
|
||||
else:
|
||||
report_writer = ReportWriterNoOp()
|
||||
|
||||
photosdb = PhotosDB(options.db, verbose=verbose)
|
||||
if options.dry_run:
|
||||
export_db = ExportDBInMemory(exportdb, export_dir)
|
||||
fileutil = FileUtilNoOp
|
||||
else:
|
||||
export_db = ExportDB(exportdb, export_dir)
|
||||
fileutil = FileUtil
|
||||
|
||||
# get_exported_files is a generator which returns tuple of (uuid, filepath)
|
||||
files = list(export_db.get_exported_files())
|
||||
# filter out sidecar files
|
||||
files = [
|
||||
(u, f)
|
||||
for u, f in files
|
||||
if pathlib.Path(f).suffix.lower() not in [".json", ".xmp"]
|
||||
]
|
||||
total = len(files)
|
||||
count = 1
|
||||
all_results = ExportResults()
|
||||
with rich_progress(console=get_verbose_console(), mock=options.verbose) as progress:
|
||||
task = progress.add_task("Processing files", total=total)
|
||||
for uuid, file in files:
|
||||
if not pathlib.Path(file).exists():
|
||||
verbose(f"Skipping missing file [filepath]{file}[/]")
|
||||
report_writer.write(ExportResults(missing=[file]))
|
||||
continue
|
||||
# zzz put in check for hardlink
|
||||
verbose(f"Processing file [filepath]{file}[/] ([num]{count}/{total}[/num])")
|
||||
photo = photosdb.get_photo(uuid)
|
||||
export_options = ExportOptions(
|
||||
description_template=options.description_template,
|
||||
dry_run=options.dry_run,
|
||||
exiftool_flags=options.exiftool_option,
|
||||
exiftool=True,
|
||||
export_db=export_db,
|
||||
ignore_date_modified=options.ignore_date_modified,
|
||||
keyword_template=options.keyword_template,
|
||||
merge_exif_keywords=options.exiftool_merge_keywords,
|
||||
merge_exif_persons=options.exiftool_merge_persons,
|
||||
replace_keywords=options.replace_keywords,
|
||||
use_albums_as_keywords=options.album_keyword,
|
||||
use_persons_as_keywords=options.person_keyword,
|
||||
verbose=verbose,
|
||||
)
|
||||
exporter = PhotoExporter(photo)
|
||||
results = exporter.write_exiftool_metadata_to_file(
|
||||
src=file, dest=file, options=export_options
|
||||
)
|
||||
all_results += results
|
||||
|
||||
for warning_ in results.exiftool_warning:
|
||||
verbose(
|
||||
f"[warning]exiftool warning for file {warning_[0]}: {warning_[1]}"
|
||||
)
|
||||
for error_ in results.exiftool_error:
|
||||
rich_echo_error(
|
||||
f"[error]exiftool error for file {error_[0]}: {error_[1]}"
|
||||
)
|
||||
for result in results.exif_updated:
|
||||
verbose(f"Updated EXIF metadata for [filepath]{result}")
|
||||
|
||||
# update the database
|
||||
with export_db.get_file_record(file) as rec:
|
||||
rec.dest_sig = fileutil.file_sig(file)
|
||||
rec.export_options = export_options.bit_flags
|
||||
rec.exifdata = exporter.exiftool_json_sidecar(export_options)
|
||||
|
||||
report_writer.write(results)
|
||||
count += 1
|
||||
progress.advance(task)
|
||||
|
||||
photo_str_total = pluralize(total, "photo", "photos")
|
||||
summary = (
|
||||
f"Processed: [num]{total}[/] {photo_str_total}, "
|
||||
f"skipped: [num]{len(all_results.skipped)}[/], "
|
||||
f"updated EXIF data: [num]{len(all_results.exif_updated)}[/], "
|
||||
)
|
||||
verbose(summary)
|
||||
|
||||
if options.report:
|
||||
verbose(f"Wrote export report to [filepath]{report}")
|
||||
report_writer.close()
|
||||
@@ -47,6 +47,7 @@ from osxphotos.export_db import ExportDB, ExportDBInMemory
|
||||
from osxphotos.fileutil import FileUtil, FileUtilNoOp
|
||||
from osxphotos.path_utils import is_valid_filepath, sanitize_filename, sanitize_filepath
|
||||
from osxphotos.photoexporter import ExportOptions, ExportResults, PhotoExporter
|
||||
from osxphotos.photoinfo import PhotoInfoNone
|
||||
from osxphotos.photokit import (
|
||||
check_photokit_authorization,
|
||||
request_photokit_authorization,
|
||||
@@ -85,6 +86,7 @@ from .common import (
|
||||
from .help import ExportCommand, get_help_msg
|
||||
from .list import _list_libraries
|
||||
from .param_types import ExportDBType, FunctionCall, TemplateString
|
||||
from .report_writer import report_writer_factory, ReportWriterNoOp
|
||||
from .rich_progress import rich_progress
|
||||
from .verbose import get_verbose_console, time_stamp, verbose_print
|
||||
|
||||
@@ -513,8 +515,20 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
|
||||
@click.option(
|
||||
"--report",
|
||||
metavar="REPORT_FILE",
|
||||
help="Write a CSV formatted report of all files that were exported.",
|
||||
type=click.Path(),
|
||||
help="Write a report of all files that were exported. "
|
||||
"The extension of the report filename will be used to determine the format. "
|
||||
"Valid extensions are: "
|
||||
".csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). "
|
||||
"REPORT_FILE may be a template string (see Templating System), for example, "
|
||||
"--report 'export_{today.date}.csv' will write a CSV report file named with today's date. "
|
||||
"See also --append.",
|
||||
type=TemplateString(),
|
||||
)
|
||||
@click.option(
|
||||
"--append",
|
||||
is_flag=True,
|
||||
help="If used with --report, add data to existing report file instead of overwriting it. "
|
||||
"See also --report.",
|
||||
)
|
||||
@click.option(
|
||||
"--cleanup",
|
||||
@@ -687,6 +701,7 @@ def export(
|
||||
added_in_last,
|
||||
album_keyword,
|
||||
album,
|
||||
append,
|
||||
beta,
|
||||
burst,
|
||||
cleanup,
|
||||
@@ -906,6 +921,7 @@ def export(
|
||||
add_skipped_to_album = cfg.add_skipped_to_album
|
||||
album = cfg.album
|
||||
album_keyword = cfg.album_keyword
|
||||
append = cfg.append
|
||||
beta = cfg.beta
|
||||
burst = cfg.burst
|
||||
cleanup = cfg.cleanup
|
||||
@@ -1091,6 +1107,7 @@ def export(
|
||||
("jpeg_quality", ("convert_to_jpeg")),
|
||||
("missing", ("download_missing", "use_photos_export")),
|
||||
("only_new", ("update", "force_update")),
|
||||
("append", ("report")),
|
||||
]
|
||||
try:
|
||||
cfg.validate(exclusive=exclusive_options, dependent=dependent_options, cli=True)
|
||||
@@ -1149,12 +1166,11 @@ def export(
|
||||
|
||||
dest = str(pathlib.Path(dest).resolve())
|
||||
|
||||
if report and os.path.isdir(report):
|
||||
rich_click_echo(
|
||||
f"[error]report is a directory, must be file name",
|
||||
err=True,
|
||||
)
|
||||
sys.exit(1)
|
||||
if report:
|
||||
report = render_and_validate_report(report, exiftool_path, dest)
|
||||
report_writer = report_writer_factory(report, append)
|
||||
else:
|
||||
report_writer = ReportWriterNoOp()
|
||||
|
||||
# if use_photokit and not check_photokit_authorization():
|
||||
# click.echo(
|
||||
@@ -1576,6 +1592,8 @@ def export(
|
||||
export_dir=dest,
|
||||
verbose_=verbose_,
|
||||
)
|
||||
export_results.xattr_written.extend(tags_written)
|
||||
export_results.xattr_skipped.extend(tags_skipped)
|
||||
results.xattr_written.extend(tags_written)
|
||||
results.xattr_skipped.extend(tags_skipped)
|
||||
|
||||
@@ -1588,9 +1606,13 @@ def export(
|
||||
export_dir=dest,
|
||||
verbose_=verbose_,
|
||||
)
|
||||
export_results.xattr_written.extend(xattr_written)
|
||||
export_results.xattr_skipped.extend(xattr_skipped)
|
||||
results.xattr_written.extend(xattr_written)
|
||||
results.xattr_skipped.extend(xattr_skipped)
|
||||
|
||||
report_writer.write(export_results)
|
||||
|
||||
progress.advance(task)
|
||||
|
||||
# handle limit
|
||||
@@ -1603,6 +1625,9 @@ def export(
|
||||
progress.advance(task, num_photos - photo_num)
|
||||
break
|
||||
|
||||
# store results so they can be used by `osxphotos exportdb --report`
|
||||
export_db.set_export_results(results)
|
||||
|
||||
photo_str_total = pluralize(len(photos), "photo", "photos")
|
||||
if update or force_update:
|
||||
summary = (
|
||||
@@ -1662,12 +1687,15 @@ def export(
|
||||
rich_echo(
|
||||
f"Deleted: [num]{len(cleaned_files)}[/num] {file_str}, [num]{len(cleaned_dirs)}[/num] {dir_str}"
|
||||
)
|
||||
report_writer.write(
|
||||
ExportResults(deleted_files=cleaned_files, deleted_directories=cleaned_dirs)
|
||||
)
|
||||
results.deleted_files = cleaned_files
|
||||
results.deleted_directories = cleaned_dirs
|
||||
|
||||
if report:
|
||||
verbose_(f"Writing export report to [filepath]{report}")
|
||||
write_export_report(report, results)
|
||||
verbose_(f"Wrote export report to [filepath]{report}")
|
||||
report_writer.close()
|
||||
|
||||
# close export_db and write changes if needed
|
||||
if ramdb and not dry_run:
|
||||
@@ -1676,18 +1704,6 @@ def export(
|
||||
export_db.close()
|
||||
|
||||
|
||||
def _export_with_profiler(args: Dict):
|
||||
""" "Run export with cProfile"""
|
||||
try:
|
||||
args.pop("profile")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
cProfile.runctx(
|
||||
"_export(**args)", globals=globals(), locals=locals(), sort="tottime"
|
||||
)
|
||||
|
||||
|
||||
def export_photo(
|
||||
photo=None,
|
||||
dest=None,
|
||||
@@ -2437,150 +2453,6 @@ def find_files_in_branch(pathname, filename):
|
||||
return files
|
||||
|
||||
|
||||
def write_export_report(report_file, results):
|
||||
|
||||
"""write CSV report with results from export
|
||||
|
||||
Args:
|
||||
report_file: path to report file
|
||||
results: ExportResults object
|
||||
"""
|
||||
|
||||
# Collect results for reporting
|
||||
all_results = {
|
||||
result: {
|
||||
"filename": result,
|
||||
"exported": 0,
|
||||
"new": 0,
|
||||
"updated": 0,
|
||||
"skipped": 0,
|
||||
"exif_updated": 0,
|
||||
"touched": 0,
|
||||
"converted_to_jpeg": 0,
|
||||
"sidecar_xmp": 0,
|
||||
"sidecar_json": 0,
|
||||
"sidecar_exiftool": 0,
|
||||
"missing": 0,
|
||||
"error": "",
|
||||
"exiftool_warning": "",
|
||||
"exiftool_error": "",
|
||||
"extended_attributes_written": 0,
|
||||
"extended_attributes_skipped": 0,
|
||||
"cleanup_deleted_file": 0,
|
||||
"cleanup_deleted_directory": 0,
|
||||
"exported_album": "",
|
||||
}
|
||||
for result in results.all_files()
|
||||
+ results.deleted_files
|
||||
+ results.deleted_directories
|
||||
}
|
||||
|
||||
for result in results.exported:
|
||||
all_results[result]["exported"] = 1
|
||||
|
||||
for result in results.new:
|
||||
all_results[result]["new"] = 1
|
||||
|
||||
for result in results.updated:
|
||||
all_results[result]["updated"] = 1
|
||||
|
||||
for result in results.skipped:
|
||||
all_results[result]["skipped"] = 1
|
||||
|
||||
for result in results.exif_updated:
|
||||
all_results[result]["exif_updated"] = 1
|
||||
|
||||
for result in results.touched:
|
||||
all_results[result]["touched"] = 1
|
||||
|
||||
for result in results.converted_to_jpeg:
|
||||
all_results[result]["converted_to_jpeg"] = 1
|
||||
|
||||
for result in results.sidecar_xmp_written:
|
||||
all_results[result]["sidecar_xmp"] = 1
|
||||
all_results[result]["exported"] = 1
|
||||
|
||||
for result in results.sidecar_xmp_skipped:
|
||||
all_results[result]["sidecar_xmp"] = 1
|
||||
all_results[result]["skipped"] = 1
|
||||
|
||||
for result in results.sidecar_json_written:
|
||||
all_results[result]["sidecar_json"] = 1
|
||||
all_results[result]["exported"] = 1
|
||||
|
||||
for result in results.sidecar_json_skipped:
|
||||
all_results[result]["sidecar_json"] = 1
|
||||
all_results[result]["skipped"] = 1
|
||||
|
||||
for result in results.sidecar_exiftool_written:
|
||||
all_results[result]["sidecar_exiftool"] = 1
|
||||
all_results[result]["exported"] = 1
|
||||
|
||||
for result in results.sidecar_exiftool_skipped:
|
||||
all_results[result]["sidecar_exiftool"] = 1
|
||||
all_results[result]["skipped"] = 1
|
||||
|
||||
for result in results.missing:
|
||||
all_results[result]["missing"] = 1
|
||||
|
||||
for result in results.error:
|
||||
all_results[result[0]]["error"] = result[1]
|
||||
|
||||
for result in results.exiftool_warning:
|
||||
all_results[result[0]]["exiftool_warning"] = result[1]
|
||||
|
||||
for result in results.exiftool_error:
|
||||
all_results[result[0]]["exiftool_error"] = result[1]
|
||||
|
||||
for result in results.xattr_written:
|
||||
all_results[result]["extended_attributes_written"] = 1
|
||||
|
||||
for result in results.xattr_skipped:
|
||||
all_results[result]["extended_attributes_skipped"] = 1
|
||||
|
||||
for result in results.deleted_files:
|
||||
all_results[result]["cleanup_deleted_file"] = 1
|
||||
|
||||
for result in results.deleted_directories:
|
||||
all_results[result]["cleanup_deleted_directory"] = 1
|
||||
|
||||
for result, album in results.exported_album:
|
||||
all_results[result]["exported_album"] = album
|
||||
|
||||
report_columns = [
|
||||
"filename",
|
||||
"exported",
|
||||
"new",
|
||||
"updated",
|
||||
"skipped",
|
||||
"exif_updated",
|
||||
"touched",
|
||||
"converted_to_jpeg",
|
||||
"sidecar_xmp",
|
||||
"sidecar_json",
|
||||
"sidecar_exiftool",
|
||||
"missing",
|
||||
"error",
|
||||
"exiftool_warning",
|
||||
"exiftool_error",
|
||||
"extended_attributes_written",
|
||||
"extended_attributes_skipped",
|
||||
"cleanup_deleted_file",
|
||||
"cleanup_deleted_directory",
|
||||
"exported_album",
|
||||
]
|
||||
|
||||
try:
|
||||
with open(report_file, "w") as csvfile:
|
||||
writer = csv.DictWriter(csvfile, fieldnames=report_columns)
|
||||
writer.writeheader()
|
||||
for data in [result for result in all_results.values()]:
|
||||
writer.writerow(data)
|
||||
except IOError:
|
||||
rich_echo_error("[error]Could not open output file for writing"),
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def cleanup_files(dest_path, files_to_keep, fileutil, verbose_):
|
||||
"""cleanup dest_path by deleting and files and empty directories
|
||||
not in files_to_keep
|
||||
@@ -2826,3 +2698,34 @@ def run_post_command(
|
||||
rich_echo_error(
|
||||
f'[error]Error running command "{command}": {run_error}'
|
||||
)
|
||||
|
||||
|
||||
def render_and_validate_report(report: str, exiftool_path: str, export_dir: str) -> str:
|
||||
"""Render a report file template and validate the filename
|
||||
|
||||
Args:
|
||||
report: the template string
|
||||
exiftool_path: the path to the exiftool binary
|
||||
export_dir: the export directory
|
||||
|
||||
Returns:
|
||||
the rendered report filename
|
||||
|
||||
Note:
|
||||
Exits with error if the report filename is invalid
|
||||
"""
|
||||
# render report template and validate the filename
|
||||
template = PhotoTemplate(PhotoInfoNone(), exiftool_path=exiftool_path)
|
||||
render_options = RenderOptions(export_dir=export_dir)
|
||||
report_file, _ = template.render(report, options=render_options)
|
||||
report = report_file[0]
|
||||
|
||||
if os.path.isdir(report):
|
||||
rich_click_echo(
|
||||
f"[error]Report '{report}' is a directory, must be file name",
|
||||
err=True,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ from rich import print
|
||||
|
||||
from osxphotos._constants import OSXPHOTOS_EXPORT_DB
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDB
|
||||
from osxphotos.export_db import (
|
||||
MAX_EXPORT_RESULTS_DATA_ROWS,
|
||||
OSXPHOTOS_EXPORTDB_VERSION,
|
||||
ExportDB,
|
||||
)
|
||||
from osxphotos.export_db_utils import (
|
||||
export_db_check_signatures,
|
||||
export_db_get_last_run,
|
||||
@@ -19,11 +23,13 @@ from osxphotos.export_db_utils import (
|
||||
export_db_vacuum,
|
||||
)
|
||||
|
||||
from .common import OSXPHOTOS_HIDDEN
|
||||
from .export import render_and_validate_report
|
||||
from .param_types import TemplateString
|
||||
from .report_writer import report_writer_factory
|
||||
from .verbose import verbose_print
|
||||
|
||||
|
||||
@click.command(name="exportdb", hidden=OSXPHOTOS_HIDDEN)
|
||||
@click.command(name="exportdb")
|
||||
@click.option("--version", is_flag=True, help="Print export database version and exit.")
|
||||
@click.option("--vacuum", is_flag=True, help="Run VACUUM to defragment the database.")
|
||||
@click.option(
|
||||
@@ -57,6 +63,24 @@ from .verbose import verbose_print
|
||||
nargs=1,
|
||||
help="Print information about FILE_PATH contained in the database.",
|
||||
)
|
||||
@click.option(
|
||||
"--report",
|
||||
metavar="REPORT_FILE RUN_ID",
|
||||
help="Generate an export report as `osxphotos export ... --report REPORT_FILE` would have done. "
|
||||
"This allows you to re-create an export report if you didn't use the --report option "
|
||||
"when running `osxphotos export`. "
|
||||
"The extension of the report file is used to determine the format. "
|
||||
"Valid extensions are: "
|
||||
".csv (CSV file), .json (JSON), .db and .sqlite (SQLite database). "
|
||||
f"RUN_ID may be any integer from {-MAX_EXPORT_RESULTS_DATA_ROWS} to 0 specifying which run to use. "
|
||||
"For example, `--report report.csv 0` will generate a CSV report for the last run and "
|
||||
"`--report report.json -1` will generate a JSON report for the second-to-last run "
|
||||
"(one run prior to last run). "
|
||||
"REPORT_FILE may be a template string (see Templating System), for example, "
|
||||
"--report 'export_{today.date}.csv' will write a CSV report file named with today's date. "
|
||||
"See also --append.",
|
||||
type=(TemplateString(), click.IntRange(-(MAX_EXPORT_RESULTS_DATA_ROWS - 1), 0)),
|
||||
)
|
||||
@click.option(
|
||||
"--migrate",
|
||||
is_flag=True,
|
||||
@@ -72,6 +96,12 @@ from .verbose import verbose_print
|
||||
help="Optional path to export directory (if not parent of export database).",
|
||||
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
||||
)
|
||||
@click.option(
|
||||
"--append",
|
||||
is_flag=True,
|
||||
help="If used with --report, add data to existing report file instead of overwriting it. "
|
||||
"See also --report.",
|
||||
)
|
||||
@click.option("--verbose", "-V", is_flag=True, help="Print verbose output.")
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
@@ -88,9 +118,11 @@ def exportdb(
|
||||
last_run,
|
||||
save_config,
|
||||
info,
|
||||
report,
|
||||
migrate,
|
||||
sql,
|
||||
export_dir,
|
||||
append,
|
||||
verbose,
|
||||
dry_run,
|
||||
export_db,
|
||||
@@ -99,6 +131,14 @@ def exportdb(
|
||||
|
||||
verbose_ = verbose_print(verbose, rich=True)
|
||||
|
||||
# validate options and args
|
||||
if append and not report:
|
||||
print(
|
||||
"[red]Error: --append requires --report; ee --help for more information.[/]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
export_db = pathlib.Path(export_db)
|
||||
if export_db.is_dir():
|
||||
# assume it's the export folder
|
||||
@@ -112,20 +152,27 @@ def exportdb(
|
||||
export_dir = export_dir or export_db.parent
|
||||
|
||||
sub_commands = [
|
||||
version,
|
||||
check_signatures,
|
||||
update_signatures,
|
||||
touch_file,
|
||||
last_run,
|
||||
bool(save_config),
|
||||
bool(info),
|
||||
migrate,
|
||||
bool(sql),
|
||||
bool(cmd)
|
||||
for cmd in [
|
||||
check_signatures,
|
||||
info,
|
||||
last_run,
|
||||
migrate,
|
||||
report,
|
||||
save_config,
|
||||
sql,
|
||||
touch_file,
|
||||
update_signatures,
|
||||
vacuum,
|
||||
version,
|
||||
]
|
||||
]
|
||||
if sum(sub_commands) > 1:
|
||||
print("[red]Only a single sub-command may be specified at a time[/red]")
|
||||
sys.exit(1)
|
||||
|
||||
# process sub-commands
|
||||
# TODO: each of these should be a function call
|
||||
if version:
|
||||
try:
|
||||
osxphotos_ver, export_db_ver = export_db_get_version(export_db)
|
||||
@@ -221,11 +268,29 @@ def exportdb(
|
||||
sys.exit(1)
|
||||
else:
|
||||
if info_rec:
|
||||
print(info_rec.asdict())
|
||||
print(info_rec.json(indent=2))
|
||||
else:
|
||||
print(f"[red]File '{info}' not found in export database[/red]")
|
||||
sys.exit(0)
|
||||
|
||||
if report:
|
||||
exportdb = ExportDB(export_db, export_dir)
|
||||
report_template, run_id = report
|
||||
report_filename = render_and_validate_report(report_template, "", export_dir)
|
||||
export_results = exportdb.get_export_results(run_id)
|
||||
if not export_results:
|
||||
print(f"[red]No report results found for run ID {run_id}[/red]")
|
||||
sys.exit(1)
|
||||
try:
|
||||
report_writer = report_writer_factory(report_filename, append=append)
|
||||
except ValueError as e:
|
||||
print(f"[red]Error: {e}[/red]")
|
||||
sys.exit(1)
|
||||
report_writer.write(export_results)
|
||||
report_writer.close()
|
||||
print(f"Wrote report to {report_filename}")
|
||||
sys.exit(0)
|
||||
|
||||
if migrate:
|
||||
exportdb = ExportDB(export_db, export_dir)
|
||||
if upgraded := exportdb.was_upgraded:
|
||||
|
||||
@@ -6,6 +6,25 @@ from runpy import run_module, run_path
|
||||
import click
|
||||
|
||||
|
||||
class RunCommand(click.Command):
|
||||
"""Custom command that ignores unknown options so options can be passed to the run script"""
|
||||
|
||||
def make_parser(self, ctx):
|
||||
"""Creates the underlying option parser for this command."""
|
||||
parser = click.OptionParser(ctx)
|
||||
parser.ignore_unknown_options = True
|
||||
for param in self.get_params(ctx):
|
||||
param.add_to_parser(parser, ctx)
|
||||
return parser
|
||||
|
||||
def get_usage(self, ctx):
|
||||
"""Returns the help for this command;
|
||||
normally it would just return the usage string
|
||||
but in order to pass --help on to the run script,
|
||||
help for the run command is handled here"""
|
||||
return self.get_help(ctx)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("packages", nargs=-1, required=True)
|
||||
@click.option(
|
||||
@@ -30,8 +49,15 @@ def uninstall(packages, yes):
|
||||
run_module("pip", run_name="__main__")
|
||||
|
||||
|
||||
@click.command(name="run")
|
||||
@click.command(name="run", cls=RunCommand)
|
||||
# help command passed just to keep click from intercepting help
|
||||
# and allowing --help to be passed to the script being run
|
||||
@click.option("--help", "-h", is_flag=True, help="Show this message and exit")
|
||||
@click.argument("python_file", nargs=1, type=click.Path(exists=True))
|
||||
def run(python_file):
|
||||
"""Run a python file using same environment as osxphotos"""
|
||||
@click.argument("args", metavar="ARGS", nargs=-1)
|
||||
def run(python_file, help, args):
|
||||
"""Run a python file using same environment as osxphotos.
|
||||
Any args are made available to the python file."""
|
||||
# drop first two arguments, which are the osxphotos script and run command
|
||||
sys.argv = sys.argv[2:]
|
||||
run_path(python_file, run_name="__main__")
|
||||
|
||||
372
osxphotos/cli/report_writer.py
Normal file
372
osxphotos/cli/report_writer.py
Normal file
@@ -0,0 +1,372 @@
|
||||
"""Report writer for the --report option of `osxphotos export`"""
|
||||
|
||||
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import sqlite3
|
||||
from abc import ABC, abstractmethod
|
||||
from contextlib import suppress
|
||||
from typing import Union, Dict
|
||||
|
||||
from osxphotos.photoexporter import ExportResults
|
||||
from osxphotos.export_db import OSXPHOTOS_ABOUT_STRING
|
||||
|
||||
__all__ = [
|
||||
"report_writer_factory",
|
||||
"ReportWriterABC",
|
||||
"ReportWriterCSV",
|
||||
"ReportWriterSqlite",
|
||||
"ReportWriterNoOp",
|
||||
]
|
||||
|
||||
|
||||
class ReportWriterABC(ABC):
|
||||
"""Abstract base class for report writers"""
|
||||
|
||||
@abstractmethod
|
||||
def write(self, export_results: ExportResults):
|
||||
"""Write results to the output file"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def close(self):
|
||||
"""Close the output file"""
|
||||
pass
|
||||
|
||||
|
||||
class ReportWriterNoOp(ABC):
|
||||
"""Report writer that does nothing"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def write(self, export_results: ExportResults):
|
||||
"""Write results to the output file"""
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
"""Close the output file"""
|
||||
pass
|
||||
|
||||
|
||||
class ReportWriterCSV(ReportWriterABC):
|
||||
"""Write CSV report file"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, bytes, os.PathLike], append: bool = False
|
||||
):
|
||||
self.output_file = output_file
|
||||
self.append = append
|
||||
|
||||
report_columns = [
|
||||
"datetime",
|
||||
"filename",
|
||||
"exported",
|
||||
"new",
|
||||
"updated",
|
||||
"skipped",
|
||||
"exif_updated",
|
||||
"touched",
|
||||
"converted_to_jpeg",
|
||||
"sidecar_xmp",
|
||||
"sidecar_json",
|
||||
"sidecar_exiftool",
|
||||
"missing",
|
||||
"error",
|
||||
"exiftool_warning",
|
||||
"exiftool_error",
|
||||
"extended_attributes_written",
|
||||
"extended_attributes_skipped",
|
||||
"cleanup_deleted_file",
|
||||
"cleanup_deleted_directory",
|
||||
"exported_album",
|
||||
]
|
||||
|
||||
mode = "a" if append else "w"
|
||||
self._output_fh = open(self.output_file, mode)
|
||||
|
||||
self._csv_writer = csv.DictWriter(self._output_fh, fieldnames=report_columns)
|
||||
if not append:
|
||||
self._csv_writer.writeheader()
|
||||
|
||||
def write(self, export_results: ExportResults):
|
||||
"""Write results to the output file"""
|
||||
all_results = prepare_results_for_writing(export_results)
|
||||
for data in list(all_results.values()):
|
||||
self._csv_writer.writerow(data)
|
||||
self._output_fh.flush()
|
||||
|
||||
def close(self):
|
||||
"""Close the output file"""
|
||||
self._output_fh.close()
|
||||
|
||||
def __del__(self):
|
||||
with suppress(Exception):
|
||||
self._output_fh.close()
|
||||
|
||||
|
||||
class ReportWriterJSON(ReportWriterABC):
|
||||
"""Write JSON report file"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, bytes, os.PathLike], append: bool = False
|
||||
):
|
||||
self.output_file = output_file
|
||||
self.append = append
|
||||
self.indent = 4
|
||||
|
||||
self._first_record_written = False
|
||||
if append:
|
||||
with open(self.output_file, "r") as fh:
|
||||
existing_data = json.load(fh)
|
||||
self._output_fh = open(self.output_file, "w")
|
||||
self._output_fh.write("[")
|
||||
for data in existing_data:
|
||||
self._output_fh.write(json.dumps(data, indent=self.indent))
|
||||
self._output_fh.write(",\n")
|
||||
else:
|
||||
self._output_fh = open(self.output_file, "w")
|
||||
self._output_fh.write("[")
|
||||
|
||||
def write(self, export_results: ExportResults):
|
||||
"""Write results to the output file"""
|
||||
all_results = prepare_results_for_writing(export_results, bool_values=True)
|
||||
for data in list(all_results.values()):
|
||||
if self._first_record_written:
|
||||
self._output_fh.write(",\n")
|
||||
else:
|
||||
self._first_record_written = True
|
||||
self._output_fh.write(json.dumps(data, indent=self.indent))
|
||||
self._output_fh.flush()
|
||||
|
||||
def close(self):
|
||||
"""Close the output file"""
|
||||
self._output_fh.write("]")
|
||||
self._output_fh.close()
|
||||
|
||||
def __del__(self):
|
||||
with suppress(Exception):
|
||||
self.close()
|
||||
|
||||
|
||||
class ReportWriterSQLite(ReportWriterABC):
|
||||
"""Write sqlite report file"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, bytes, os.PathLike], append: bool = False
|
||||
):
|
||||
self.output_file = output_file
|
||||
self.append = append
|
||||
|
||||
if not append:
|
||||
with suppress(FileNotFoundError):
|
||||
os.unlink(self.output_file)
|
||||
|
||||
self._conn = sqlite3.connect(self.output_file)
|
||||
self._create_tables()
|
||||
|
||||
def write(self, export_results: ExportResults):
|
||||
"""Write results to the output file"""
|
||||
|
||||
all_results = prepare_results_for_writing(export_results)
|
||||
for data in list(all_results.values()):
|
||||
cursor = self._conn.cursor()
|
||||
cursor.execute(
|
||||
"INSERT INTO report "
|
||||
"(datetime, filename, exported, new, updated, skipped, exif_updated, touched, converted_to_jpeg, sidecar_xmp, sidecar_json, sidecar_exiftool, missing, error, exiftool_warning, exiftool_error, extended_attributes_written, extended_attributes_skipped, cleanup_deleted_file, cleanup_deleted_directory, exported_album) "
|
||||
"VALUES "
|
||||
"(:datetime, :filename, :exported, :new, :updated, :skipped, :exif_updated, :touched, :converted_to_jpeg, :sidecar_xmp, :sidecar_json, :sidecar_exiftool, :missing, :error, :exiftool_warning, :exiftool_error, :extended_attributes_written, :extended_attributes_skipped, :cleanup_deleted_file, :cleanup_deleted_directory, :exported_album);",
|
||||
data,
|
||||
)
|
||||
self._conn.commit()
|
||||
|
||||
def close(self):
|
||||
"""Close the output file"""
|
||||
self._conn.close()
|
||||
|
||||
def _create_tables(self):
|
||||
c = self._conn.cursor()
|
||||
c.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS report (
|
||||
datetime text,
|
||||
filename text,
|
||||
exported integer,
|
||||
new integer,
|
||||
updated integer,
|
||||
skipped integer,
|
||||
exif_updated integer,
|
||||
touched integer,
|
||||
converted_to_jpeg integer,
|
||||
sidecar_xmp integer,
|
||||
sidecar_json integer,
|
||||
sidecar_exiftool integer,
|
||||
missing integer,
|
||||
error text,
|
||||
exiftool_warning text,
|
||||
exiftool_error text,
|
||||
extended_attributes_written integer,
|
||||
extended_attributes_skipped integer,
|
||||
cleanup_deleted_file integer,
|
||||
cleanup_deleted_directory integer,
|
||||
exported_album text
|
||||
)
|
||||
"""
|
||||
)
|
||||
c.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS about (
|
||||
id INTEGER PRIMARY KEY,
|
||||
about TEXT
|
||||
);"""
|
||||
)
|
||||
c.execute(
|
||||
"INSERT INTO about(about) VALUES (?);",
|
||||
(f"OSXPhotos Export Report. {OSXPHOTOS_ABOUT_STRING}",),
|
||||
)
|
||||
|
||||
self._conn.commit()
|
||||
|
||||
def __del__(self):
|
||||
with suppress(Exception):
|
||||
self.close()
|
||||
|
||||
|
||||
def prepare_results_for_writing(
|
||||
export_results: ExportResults, bool_values: bool = False
|
||||
) -> Dict:
|
||||
"""Return all results for writing to report
|
||||
|
||||
Args:
|
||||
export_results: ExportResults object
|
||||
bool_values: Return a boolean value instead of a integer (e.g. for use with JSON)
|
||||
|
||||
Returns:
|
||||
Dict: All results
|
||||
"""
|
||||
false = False if bool_values else 0
|
||||
true = True if bool_values else 1
|
||||
|
||||
all_results = {}
|
||||
for result in (
|
||||
export_results.all_files()
|
||||
+ export_results.deleted_files
|
||||
+ export_results.deleted_directories
|
||||
):
|
||||
result = str(result)
|
||||
if result not in all_results:
|
||||
all_results[str(result)] = {
|
||||
"datetime": export_results.datetime,
|
||||
"filename": str(result),
|
||||
"exported": false,
|
||||
"new": false,
|
||||
"updated": false,
|
||||
"skipped": false,
|
||||
"exif_updated": false,
|
||||
"touched": false,
|
||||
"converted_to_jpeg": false,
|
||||
"sidecar_xmp": false,
|
||||
"sidecar_json": false,
|
||||
"sidecar_exiftool": false,
|
||||
"missing": false,
|
||||
"error": "",
|
||||
"exiftool_warning": "",
|
||||
"exiftool_error": "",
|
||||
"extended_attributes_written": false,
|
||||
"extended_attributes_skipped": false,
|
||||
"cleanup_deleted_file": false,
|
||||
"cleanup_deleted_directory": false,
|
||||
"exported_album": "",
|
||||
}
|
||||
|
||||
for result in export_results.exported:
|
||||
all_results[str(result)]["exported"] = true
|
||||
|
||||
for result in export_results.new:
|
||||
all_results[str(result)]["new"] = true
|
||||
|
||||
for result in export_results.updated:
|
||||
all_results[str(result)]["updated"] = true
|
||||
|
||||
for result in export_results.skipped:
|
||||
all_results[str(result)]["skipped"] = true
|
||||
|
||||
for result in export_results.exif_updated:
|
||||
all_results[str(result)]["exif_updated"] = true
|
||||
|
||||
for result in export_results.touched:
|
||||
all_results[str(result)]["touched"] = true
|
||||
|
||||
for result in export_results.converted_to_jpeg:
|
||||
all_results[str(result)]["converted_to_jpeg"] = true
|
||||
|
||||
for result in export_results.sidecar_xmp_written:
|
||||
all_results[str(result)]["sidecar_xmp"] = true
|
||||
all_results[str(result)]["exported"] = true
|
||||
|
||||
for result in export_results.sidecar_xmp_skipped:
|
||||
all_results[str(result)]["sidecar_xmp"] = true
|
||||
all_results[str(result)]["skipped"] = true
|
||||
|
||||
for result in export_results.sidecar_json_written:
|
||||
all_results[str(result)]["sidecar_json"] = true
|
||||
all_results[str(result)]["exported"] = true
|
||||
|
||||
for result in export_results.sidecar_json_skipped:
|
||||
all_results[str(result)]["sidecar_json"] = true
|
||||
all_results[str(result)]["skipped"] = true
|
||||
|
||||
for result in export_results.sidecar_exiftool_written:
|
||||
all_results[str(result)]["sidecar_exiftool"] = true
|
||||
all_results[str(result)]["exported"] = true
|
||||
|
||||
for result in export_results.sidecar_exiftool_skipped:
|
||||
all_results[str(result)]["sidecar_exiftool"] = true
|
||||
all_results[str(result)]["skipped"] = true
|
||||
|
||||
for result in export_results.missing:
|
||||
all_results[str(result)]["missing"] = true
|
||||
|
||||
for result in export_results.error:
|
||||
all_results[str(result[0])]["error"] = result[1]
|
||||
|
||||
for result in export_results.exiftool_warning:
|
||||
all_results[str(result[0])]["exiftool_warning"] = result[1]
|
||||
|
||||
for result in export_results.exiftool_error:
|
||||
all_results[str(result[0])]["exiftool_error"] = result[1]
|
||||
|
||||
for result in export_results.xattr_written:
|
||||
all_results[str(result)]["extended_attributes_written"] = true
|
||||
|
||||
for result in export_results.xattr_skipped:
|
||||
all_results[str(result)]["extended_attributes_skipped"] = true
|
||||
|
||||
for result in export_results.deleted_files:
|
||||
all_results[str(result)]["cleanup_deleted_file"] = true
|
||||
|
||||
for result in export_results.deleted_directories:
|
||||
all_results[str(result)]["cleanup_deleted_directory"] = true
|
||||
|
||||
for result, album in export_results.exported_album:
|
||||
all_results[str(result)]["exported_album"] = album
|
||||
|
||||
return all_results
|
||||
|
||||
|
||||
def report_writer_factory(
|
||||
output_file: Union[str, bytes, os.PathLike], append: bool = False
|
||||
) -> ReportWriterABC:
|
||||
"""Return a ReportWriter instance appropriate for the output file type"""
|
||||
output_type = os.path.splitext(output_file)[1]
|
||||
output_type = output_type.lower()[1:]
|
||||
if output_type == "csv":
|
||||
return ReportWriterCSV(output_file, append)
|
||||
elif output_type == "json":
|
||||
return ReportWriterJSON(output_file, append)
|
||||
elif output_type in ["sqlite", "db"]:
|
||||
return ReportWriterSQLite(output_file, append)
|
||||
else:
|
||||
raise ValueError(f"Unknown report file type: {output_file}")
|
||||
@@ -57,8 +57,8 @@ class ConfigOptions:
|
||||
setattr(self, attr, self._attrs[attr])
|
||||
else:
|
||||
setattr(self, attr, arg)
|
||||
except KeyError:
|
||||
raise KeyError(f"Missing argument: {attr}")
|
||||
except KeyError as e:
|
||||
raise KeyError(f"Missing argument: {attr}") from e
|
||||
|
||||
def validate(self, exclusive=None, inclusive=None, dependent=None, cli=False):
|
||||
"""validate combinations of otions
|
||||
@@ -153,9 +153,26 @@ class ConfigOptions:
|
||||
ConfigOptionsLoadError if there are any errors during the parsing of the TOML file
|
||||
"""
|
||||
loaded = toml.load(filename)
|
||||
return self._load_from_toml_dict(loaded, override)
|
||||
|
||||
def load_from_str(self, str, override=False):
|
||||
"""Load options from a TOML str.
|
||||
|
||||
Args:
|
||||
str: TOML str
|
||||
override: bool; if True, values in the TOML str will override values already set in the instance
|
||||
|
||||
Raises:
|
||||
ConfigOptionsLoadError if there are any errors during the parsing of the TOML str
|
||||
"""
|
||||
loaded = toml.loads(str)
|
||||
return self._load_from_toml_dict(loaded, override)
|
||||
|
||||
def _load_from_toml_dict(self, loaded, override):
|
||||
"""Load options from a TOML dict (as returned by toml.load or toml.loads)"""
|
||||
name = self._name
|
||||
if name not in loaded:
|
||||
raise ConfigOptionsLoadError(f"[{name}] section missing from {filename}")
|
||||
raise ConfigOptionsLoadError(f"[{name}] section missing from config file")
|
||||
|
||||
for attr in loaded[name]:
|
||||
if attr not in self._attrs:
|
||||
|
||||
Binary file not shown.
@@ -84,8 +84,7 @@ def terminate_exiftool():
|
||||
@lru_cache(maxsize=1)
|
||||
def get_exiftool_path():
|
||||
"""return path of exiftool, cache result"""
|
||||
exiftool_path = shutil.which("exiftool")
|
||||
if exiftool_path:
|
||||
if exiftool_path := shutil.which("exiftool"):
|
||||
return exiftool_path.rstrip()
|
||||
else:
|
||||
raise FileNotFoundError(
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
""" Helper class for managing database used by PhotoExporter for tracking state of exports and updates """
|
||||
|
||||
|
||||
import contextlib
|
||||
import datetime
|
||||
import gzip
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import pickle
|
||||
import sqlite3
|
||||
import sys
|
||||
import time
|
||||
from contextlib import suppress
|
||||
from io import StringIO
|
||||
from sqlite3 import Error
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Optional, Tuple, Union
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
|
||||
from tenacity import retry, stop_after_attempt
|
||||
|
||||
@@ -27,12 +30,42 @@ __all__ = [
|
||||
"ExportDBTemp",
|
||||
]
|
||||
|
||||
OSXPHOTOS_EXPORTDB_VERSION = "6.0"
|
||||
OSXPHOTOS_EXPORTDB_VERSION = "7.0"
|
||||
OSXPHOTOS_ABOUT_STRING = f"Created by osxphotos version {__version__} (https://github.com/RhetTbull/osxphotos) on {datetime.datetime.now()}"
|
||||
|
||||
# max retry attempts for methods which use tenacity.retry
|
||||
MAX_RETRY_ATTEMPTS = 5
|
||||
|
||||
# maximum number of export results rows to save
|
||||
MAX_EXPORT_RESULTS_DATA_ROWS = 10
|
||||
|
||||
|
||||
def pickle_and_zip(data: Any) -> bytes:
|
||||
"""
|
||||
Pickle and gzip data.
|
||||
|
||||
Args:
|
||||
data: data to pickle and gzip (must be pickle-able)
|
||||
|
||||
Returns:
|
||||
bytes of gzipped pickled data
|
||||
"""
|
||||
pickled = pickle.dumps(data)
|
||||
return gzip.compress(pickled)
|
||||
|
||||
|
||||
def unzip_and_unpickle(data: bytes) -> Any:
|
||||
"""
|
||||
Unzip and unpickle data.
|
||||
|
||||
Args:
|
||||
data: data to unzip and unpickle
|
||||
|
||||
Returns:
|
||||
unpickled data
|
||||
"""
|
||||
return pickle.loads(gzip.decompress(data))
|
||||
|
||||
|
||||
class ExportDB:
|
||||
"""Interface to sqlite3 database used to store state information for osxphotos export command"""
|
||||
@@ -192,6 +225,77 @@ class ExportDB:
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def set_export_results(self, results):
|
||||
"""Store export results in database; data is pickled and gzipped for storage"""
|
||||
|
||||
results_data = pickle_and_zip(results)
|
||||
|
||||
conn = self._conn
|
||||
try:
|
||||
dt = datetime.datetime.now().isoformat()
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"""
|
||||
UPDATE export_results_data
|
||||
SET datetime = ?,
|
||||
export_results = ?
|
||||
WHERE datetime = (SELECT MIN(datetime) FROM export_results_data);
|
||||
""",
|
||||
(dt, results_data),
|
||||
)
|
||||
conn.commit()
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def get_export_results(self, run: int = 0):
|
||||
"""Retrieve export results from database
|
||||
|
||||
Args:
|
||||
run: which run to retrieve results for;
|
||||
0 = most recent run, -1 = previous run, -2 = run prior to that, etc.
|
||||
|
||||
Returns:
|
||||
ExportResults object or None if no results found
|
||||
"""
|
||||
if run > 0:
|
||||
raise ValueError("run must be 0 or negative")
|
||||
run = -run
|
||||
|
||||
conn = self._conn
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"""
|
||||
SELECT export_results
|
||||
FROM export_results_data
|
||||
ORDER BY datetime DESC
|
||||
""",
|
||||
)
|
||||
rows = c.fetchall()
|
||||
try:
|
||||
data = rows[run][0]
|
||||
results = unzip_and_unpickle(data) if data else None
|
||||
except IndexError:
|
||||
results = None
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
results = None
|
||||
return results
|
||||
|
||||
def get_exported_files(self):
|
||||
"""Returns tuple of (uuid, filepath) for all paths of all exported files tracked in the database"""
|
||||
conn = self._conn
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT uuid, filepath FROM export_data")
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
return
|
||||
|
||||
while row := c.fetchone():
|
||||
yield row[0], os.path.join(self.export_dir, row[1])
|
||||
return
|
||||
|
||||
def close(self):
|
||||
"""close the database connection"""
|
||||
try:
|
||||
@@ -209,7 +313,7 @@ class ExportDB:
|
||||
if not os.path.isfile(dbfile):
|
||||
conn = self._get_db_connection(dbfile)
|
||||
if not conn:
|
||||
raise Exception("Error getting connection to database {dbfile}")
|
||||
raise Exception(f"Error getting connection to database {dbfile}")
|
||||
self._create_or_migrate_db_tables(conn)
|
||||
self.was_created = True
|
||||
self.was_upgraded = ()
|
||||
@@ -362,12 +466,16 @@ class ExportDB:
|
||||
# create export_data table
|
||||
self._migrate_5_0_to_6_0(conn)
|
||||
|
||||
if version[1] < "7.0":
|
||||
# create report_data table
|
||||
self._migrate_6_0_to_7_0(conn)
|
||||
|
||||
conn.execute("VACUUM;")
|
||||
conn.commit()
|
||||
|
||||
def __del__(self):
|
||||
"""ensure the database connection is closed"""
|
||||
with contextlib.suppress(Exception):
|
||||
with suppress(Exception):
|
||||
self._conn.close()
|
||||
|
||||
def _insert_run_info(self):
|
||||
@@ -556,6 +664,29 @@ class ExportDB:
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def _migrate_6_0_to_7_0(self, conn):
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS export_results_data (
|
||||
id INTEGER PRIMARY KEY,
|
||||
datetime TEXT,
|
||||
export_results BLOB
|
||||
);"""
|
||||
)
|
||||
# pre-populate report_data table with blank fields
|
||||
# ExportDB will use these as circular buffer always writing to the oldest record
|
||||
for _ in range(MAX_EXPORT_RESULTS_DATA_ROWS):
|
||||
c.execute(
|
||||
"""INSERT INTO export_results_data (datetime, export_results) VALUES (?, ?);""",
|
||||
(datetime.datetime.now().isoformat(), b""),
|
||||
)
|
||||
# sleep a tiny bit just to ensure time stamps increment
|
||||
time.sleep(0.001)
|
||||
conn.commit()
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def _perform_db_maintenace(self, conn):
|
||||
"""Perform database maintenance"""
|
||||
try:
|
||||
@@ -630,7 +761,7 @@ class ExportDBInMemory(ExportDB):
|
||||
except Error as e:
|
||||
logging.warning(e)
|
||||
|
||||
def _open_export_db(self, dbfile):
|
||||
def _open_export_db(self, dbfile): # sourcery skip: raise-specific-error
|
||||
"""open export database and return a db connection
|
||||
returns: connection to the database
|
||||
"""
|
||||
@@ -681,7 +812,7 @@ class ExportDBInMemory(ExportDB):
|
||||
|
||||
def __del__(self):
|
||||
"""close the database connection"""
|
||||
with contextlib.suppress(Error):
|
||||
with suppress(Error):
|
||||
self.close()
|
||||
|
||||
|
||||
@@ -933,6 +1064,10 @@ class ExportRecord:
|
||||
"photoinfo": photoinfo,
|
||||
}
|
||||
|
||||
def json(self, indent=None):
|
||||
"""Return json of self"""
|
||||
return json.dumps(self.asdict(), indent=indent)
|
||||
|
||||
def __enter__(self):
|
||||
self._context_manager = True
|
||||
return self
|
||||
|
||||
@@ -12,10 +12,11 @@ from rich import print
|
||||
|
||||
from ._constants import OSXPHOTOS_EXPORT_DB
|
||||
from ._version import __version__
|
||||
from .utils import noop
|
||||
from .configoptions import ConfigOptions
|
||||
from .export_db import OSXPHOTOS_EXPORTDB_VERSION, ExportDB
|
||||
from .fileutil import FileUtil
|
||||
from .photosdb import PhotosDB
|
||||
from .utils import noop
|
||||
|
||||
__all__ = [
|
||||
"export_db_check_signatures",
|
||||
@@ -125,6 +126,23 @@ def export_db_save_config_to_file(
|
||||
f.write(row[0])
|
||||
|
||||
|
||||
def export_db_get_config(
|
||||
export_db: Union[str, pathlib.Path], config: ConfigOptions, override=False
|
||||
) -> ConfigOptions:
|
||||
"""Load last run config to config
|
||||
|
||||
Args:
|
||||
export_db: path to export database
|
||||
override: if True, any loaded config values will overwrite existing values in config
|
||||
"""
|
||||
conn = sqlite3.connect(str(export_db))
|
||||
c = conn.cursor()
|
||||
row = c.execute("SELECT config FROM config ORDER BY id DESC LIMIT 1;").fetchone()
|
||||
if not row:
|
||||
return ValueError("No config found in export_db")
|
||||
return config.load_from_str(row[0], override=override)
|
||||
|
||||
|
||||
def export_db_check_signatures(
|
||||
dbfile: Union[str, pathlib.Path],
|
||||
export_dir: Union[str, pathlib.Path],
|
||||
|
||||
@@ -10,6 +10,7 @@ import re
|
||||
import typing as t
|
||||
from collections import namedtuple # pylint: disable=syntax-error
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
import photoscript
|
||||
@@ -238,59 +239,62 @@ class ExportResults:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
exported=None,
|
||||
new=None,
|
||||
updated=None,
|
||||
skipped=None,
|
||||
exif_updated=None,
|
||||
touched=None,
|
||||
to_touch=None,
|
||||
converted_to_jpeg=None,
|
||||
sidecar_json_written=None,
|
||||
sidecar_json_skipped=None,
|
||||
sidecar_exiftool_written=None,
|
||||
sidecar_exiftool_skipped=None,
|
||||
sidecar_xmp_written=None,
|
||||
sidecar_xmp_skipped=None,
|
||||
missing=None,
|
||||
error=None,
|
||||
exiftool_warning=None,
|
||||
exiftool_error=None,
|
||||
xattr_written=None,
|
||||
xattr_skipped=None,
|
||||
deleted_files=None,
|
||||
deleted_directories=None,
|
||||
deleted_files=None,
|
||||
error=None,
|
||||
exif_updated=None,
|
||||
exiftool_error=None,
|
||||
exiftool_warning=None,
|
||||
exported_album=None,
|
||||
skipped_album=None,
|
||||
missing_album=None,
|
||||
exported=None,
|
||||
metadata_changed=None,
|
||||
missing_album=None,
|
||||
missing=None,
|
||||
new=None,
|
||||
sidecar_exiftool_skipped=None,
|
||||
sidecar_exiftool_written=None,
|
||||
sidecar_json_skipped=None,
|
||||
sidecar_json_written=None,
|
||||
sidecar_xmp_skipped=None,
|
||||
sidecar_xmp_written=None,
|
||||
skipped_album=None,
|
||||
skipped=None,
|
||||
to_touch=None,
|
||||
touched=None,
|
||||
updated=None,
|
||||
xattr_skipped=None,
|
||||
xattr_written=None,
|
||||
):
|
||||
self.exported = exported or []
|
||||
self.new = new or []
|
||||
self.updated = updated or []
|
||||
self.skipped = skipped or []
|
||||
self.exif_updated = exif_updated or []
|
||||
self.touched = touched or []
|
||||
self.to_touch = to_touch or []
|
||||
|
||||
self.datetime = datetime.now().isoformat()
|
||||
|
||||
self.converted_to_jpeg = converted_to_jpeg or []
|
||||
self.sidecar_json_written = sidecar_json_written or []
|
||||
self.sidecar_json_skipped = sidecar_json_skipped or []
|
||||
self.sidecar_exiftool_written = sidecar_exiftool_written or []
|
||||
self.sidecar_exiftool_skipped = sidecar_exiftool_skipped or []
|
||||
self.sidecar_xmp_written = sidecar_xmp_written or []
|
||||
self.sidecar_xmp_skipped = sidecar_xmp_skipped or []
|
||||
self.missing = missing or []
|
||||
self.error = error or []
|
||||
self.exiftool_warning = exiftool_warning or []
|
||||
self.exiftool_error = exiftool_error or []
|
||||
self.xattr_written = xattr_written or []
|
||||
self.xattr_skipped = xattr_skipped or []
|
||||
self.deleted_files = deleted_files or []
|
||||
self.deleted_directories = deleted_directories or []
|
||||
self.deleted_files = deleted_files or []
|
||||
self.error = error or []
|
||||
self.exif_updated = exif_updated or []
|
||||
self.exiftool_error = exiftool_error or []
|
||||
self.exiftool_warning = exiftool_warning or []
|
||||
self.exported = exported or []
|
||||
self.exported_album = exported_album or []
|
||||
self.skipped_album = skipped_album or []
|
||||
self.missing_album = missing_album or []
|
||||
self.metadata_changed = metadata_changed or []
|
||||
self.missing = missing or []
|
||||
self.missing_album = missing_album or []
|
||||
self.new = new or []
|
||||
self.sidecar_exiftool_skipped = sidecar_exiftool_skipped or []
|
||||
self.sidecar_exiftool_written = sidecar_exiftool_written or []
|
||||
self.sidecar_json_skipped = sidecar_json_skipped or []
|
||||
self.sidecar_json_written = sidecar_json_written or []
|
||||
self.sidecar_xmp_skipped = sidecar_xmp_skipped or []
|
||||
self.sidecar_xmp_written = sidecar_xmp_written or []
|
||||
self.skipped = skipped or []
|
||||
self.skipped_album = skipped_album or []
|
||||
self.to_touch = to_touch or []
|
||||
self.touched = touched or []
|
||||
self.updated = updated or []
|
||||
self.xattr_skipped = xattr_skipped or []
|
||||
self.xattr_written = xattr_written or []
|
||||
|
||||
def all_files(self):
|
||||
"""return all filenames contained in results"""
|
||||
@@ -348,7 +352,8 @@ class ExportResults:
|
||||
def __str__(self):
|
||||
return (
|
||||
"ExportResults("
|
||||
+ f"exported={self.exported}"
|
||||
+ f"datetime={self.datetime}"
|
||||
+ f",exported={self.exported}"
|
||||
+ f",new={self.new}"
|
||||
+ f",updated={self.updated}"
|
||||
+ f",skipped={self.skipped}"
|
||||
@@ -730,7 +735,7 @@ class PhotoExporter:
|
||||
return ShouldUpdate.EXPORT_OPTIONS_DIFFERENT
|
||||
|
||||
if options.exiftool:
|
||||
current_exifdata = self._exiftool_json_sidecar(options=options)
|
||||
current_exifdata = self.exiftool_json_sidecar(options=options)
|
||||
rv = current_exifdata != file_record.exifdata
|
||||
# if using exiftool, don't need to continue checking edited below
|
||||
# as exiftool will be used to update edited file
|
||||
@@ -1138,7 +1143,7 @@ class PhotoExporter:
|
||||
# point src to the tmp_file so that the original source is not modified
|
||||
# and the export grabs the new file
|
||||
src = tmp_file
|
||||
exif_results = self._write_exif_metadata_to_file(
|
||||
exif_results = self.write_exiftool_metadata_to_file(
|
||||
src, dest, options=options
|
||||
)
|
||||
|
||||
@@ -1180,7 +1185,7 @@ class PhotoExporter:
|
||||
if not options.ignore_signature:
|
||||
rec.dest_sig = fileutil.file_sig(dest)
|
||||
if options.exiftool:
|
||||
rec.exifdata = self._exiftool_json_sidecar(options)
|
||||
rec.exifdata = self.exiftool_json_sidecar(options)
|
||||
if self.photo.hexdigest != rec.digest:
|
||||
results.metadata_changed = [dest_str]
|
||||
rec.digest = self.photo.hexdigest
|
||||
@@ -1315,7 +1320,7 @@ class PhotoExporter:
|
||||
sidecar_filename = dest.parent / pathlib.Path(
|
||||
f"{dest.stem}{dest_suffix}.json"
|
||||
)
|
||||
sidecar_str = self._exiftool_json_sidecar(
|
||||
sidecar_str = self.exiftool_json_sidecar(
|
||||
filename=dest.name, options=options
|
||||
)
|
||||
sidecars.append(
|
||||
@@ -1332,7 +1337,7 @@ class PhotoExporter:
|
||||
sidecar_filename = dest.parent / pathlib.Path(
|
||||
f"{dest.stem}{dest_suffix}.json"
|
||||
)
|
||||
sidecar_str = self._exiftool_json_sidecar(
|
||||
sidecar_str = self.exiftool_json_sidecar(
|
||||
tag_groups=False, filename=dest.name, options=options
|
||||
)
|
||||
sidecars.append(
|
||||
@@ -1431,19 +1436,20 @@ class PhotoExporter:
|
||||
|
||||
return results
|
||||
|
||||
def _write_exif_metadata_to_file(
|
||||
def write_exiftool_metadata_to_file(
|
||||
self,
|
||||
src,
|
||||
dest,
|
||||
options: ExportOptions,
|
||||
) -> ExportResults:
|
||||
"""Write exif metadata to file using exiftool
|
||||
"""Write exif metadata to src file using exiftool
|
||||
|
||||
Note: this method modifies src so src must be a copy of the original file;
|
||||
Caution: This method modifies *src*, not *dest*,
|
||||
so src must be a copy of the original file if you don't want the source modified;
|
||||
it also does not write to dest (dest is the intended destination for purposes of
|
||||
referencing the export database. This allows the exiftool update to be done on the
|
||||
local machine prior to being copied to the export destination which may be on a
|
||||
network drive or other slower external storage."""
|
||||
network drive or other slower external storage)."""
|
||||
|
||||
verbose = options.verbose or self._verbose
|
||||
exiftool_results = ExportResults()
|
||||
@@ -1486,7 +1492,7 @@ class PhotoExporter:
|
||||
old_data = exif_record.exifdata if exif_record else None
|
||||
if old_data is not None:
|
||||
old_data = json.loads(old_data)[0]
|
||||
current_data = json.loads(self._exiftool_json_sidecar(options=options))
|
||||
current_data = json.loads(self.exiftool_json_sidecar(options=options))
|
||||
current_data = current_data[0]
|
||||
if old_data != current_data:
|
||||
files_are_different = True
|
||||
@@ -1826,7 +1832,7 @@ class PhotoExporter:
|
||||
pass
|
||||
return persons
|
||||
|
||||
def _exiftool_json_sidecar(
|
||||
def exiftool_json_sidecar(
|
||||
self,
|
||||
options: t.Optional[ExportOptions] = None,
|
||||
tag_groups: bool = True,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
""" Custom template system for osxphotos, implements metadata template language (MTL) """
|
||||
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shlex
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
@@ -419,7 +419,7 @@ class PhotoTemplate:
|
||||
try:
|
||||
model = self.parser.parse(template)
|
||||
except TextXSyntaxError as e:
|
||||
raise ValueError(f"SyntaxError: {e}")
|
||||
raise ValueError(f"SyntaxError: {e}") from e
|
||||
|
||||
if not model:
|
||||
# empty string
|
||||
@@ -612,10 +612,12 @@ class PhotoTemplate:
|
||||
break
|
||||
if match:
|
||||
break
|
||||
if (match and not negation) or (negation and not match):
|
||||
return ["True"]
|
||||
else:
|
||||
return []
|
||||
|
||||
return (
|
||||
["True"]
|
||||
if (match and not negation) or (negation and not match)
|
||||
else []
|
||||
)
|
||||
|
||||
def comparison_test(test_function):
|
||||
"""Perform numerical comparisons using test_function; closure to capture conditional_val, vals, negation"""
|
||||
@@ -627,14 +629,16 @@ class PhotoTemplate:
|
||||
match = bool(
|
||||
test_function(float(vals[0]), float(conditional_value[0]))
|
||||
)
|
||||
if (match and not negation) or (negation and not match):
|
||||
return ["True"]
|
||||
else:
|
||||
return []
|
||||
|
||||
return (
|
||||
["True"]
|
||||
if (match and not negation) or (negation and not match)
|
||||
else []
|
||||
)
|
||||
except ValueError as e:
|
||||
raise ValueError(
|
||||
f"comparison operators may only be used with values that can be converted to numbers: {vals} {conditional_value}"
|
||||
)
|
||||
) from e
|
||||
|
||||
if operator in ["contains", "matches", "startswith", "endswith"]:
|
||||
# process any "or" values separated by "|"
|
||||
@@ -722,9 +726,6 @@ class PhotoTemplate:
|
||||
ValueError if no rule exists for field.
|
||||
"""
|
||||
|
||||
if self.photo.uuid is None:
|
||||
return []
|
||||
|
||||
# initialize today with current date/time if needed
|
||||
if self.today is None:
|
||||
self.today = datetime.datetime.now()
|
||||
@@ -732,7 +733,50 @@ class PhotoTemplate:
|
||||
value = None
|
||||
|
||||
# wouldn't a switch/case statement be nice...
|
||||
if field == "name":
|
||||
# handle the fields that don't require a PhotoInfo object first
|
||||
if field == "today.date":
|
||||
value = DateTimeFormatter(self.today).date
|
||||
elif field == "today.year":
|
||||
value = DateTimeFormatter(self.today).year
|
||||
elif field == "today.yy":
|
||||
value = DateTimeFormatter(self.today).yy
|
||||
elif field == "today.mm":
|
||||
value = DateTimeFormatter(self.today).mm
|
||||
elif field == "today.month":
|
||||
value = DateTimeFormatter(self.today).month
|
||||
elif field == "today.mon":
|
||||
value = DateTimeFormatter(self.today).mon
|
||||
elif field == "today.dd":
|
||||
value = DateTimeFormatter(self.today).dd
|
||||
elif field == "today.dow":
|
||||
value = DateTimeFormatter(self.today).dow
|
||||
elif field == "today.doy":
|
||||
value = DateTimeFormatter(self.today).doy
|
||||
elif field == "today.hour":
|
||||
value = DateTimeFormatter(self.today).hour
|
||||
elif field == "today.min":
|
||||
value = DateTimeFormatter(self.today).min
|
||||
elif field == "today.sec":
|
||||
value = DateTimeFormatter(self.today).sec
|
||||
elif field == "today.strftime":
|
||||
if default:
|
||||
try:
|
||||
value = self.today.strftime(default[0])
|
||||
except:
|
||||
raise ValueError(f"Invalid strftime template: '{default}'")
|
||||
else:
|
||||
value = None
|
||||
elif field in PUNCTUATION:
|
||||
value = PUNCTUATION[field]
|
||||
elif field == "osxphotos_version":
|
||||
value = __version__
|
||||
elif field == "osxphotos_cmd_line":
|
||||
value = " ".join(sys.argv)
|
||||
elif self.photo.uuid is None:
|
||||
# if no uuid, don't have a PhotoInfo object (could be PhotoInfoNone)
|
||||
# so don't try to handle any of the photo fields
|
||||
return []
|
||||
elif field == "name":
|
||||
value = pathlib.Path(self.photo.filename).stem
|
||||
elif field == "original_name":
|
||||
value = pathlib.Path(self.photo.original_filename).stem
|
||||
@@ -865,38 +909,6 @@ class PhotoTemplate:
|
||||
raise ValueError(f"Invalid strftime template: '{default}'")
|
||||
else:
|
||||
value = None
|
||||
elif field == "today.date":
|
||||
value = DateTimeFormatter(self.today).date
|
||||
elif field == "today.year":
|
||||
value = DateTimeFormatter(self.today).year
|
||||
elif field == "today.yy":
|
||||
value = DateTimeFormatter(self.today).yy
|
||||
elif field == "today.mm":
|
||||
value = DateTimeFormatter(self.today).mm
|
||||
elif field == "today.month":
|
||||
value = DateTimeFormatter(self.today).month
|
||||
elif field == "today.mon":
|
||||
value = DateTimeFormatter(self.today).mon
|
||||
elif field == "today.dd":
|
||||
value = DateTimeFormatter(self.today).dd
|
||||
elif field == "today.dow":
|
||||
value = DateTimeFormatter(self.today).dow
|
||||
elif field == "today.doy":
|
||||
value = DateTimeFormatter(self.today).doy
|
||||
elif field == "today.hour":
|
||||
value = DateTimeFormatter(self.today).hour
|
||||
elif field == "today.min":
|
||||
value = DateTimeFormatter(self.today).min
|
||||
elif field == "today.sec":
|
||||
value = DateTimeFormatter(self.today).sec
|
||||
elif field == "today.strftime":
|
||||
if default:
|
||||
try:
|
||||
value = self.today.strftime(default[0])
|
||||
except:
|
||||
raise ValueError(f"Invalid strftime template: '{default}'")
|
||||
else:
|
||||
value = None
|
||||
elif field == "place.name":
|
||||
value = self.photo.place.name if self.photo.place else None
|
||||
elif field == "place.country_code":
|
||||
@@ -982,33 +994,25 @@ class PhotoTemplate:
|
||||
elif field == "id":
|
||||
value = format_str_value(self.photo._info["pk"], subfield)
|
||||
elif field.startswith("album_seq") or field.startswith("folder_album_seq"):
|
||||
dest_path = self.dest_path
|
||||
if not dest_path:
|
||||
value = None
|
||||
else:
|
||||
if dest_path := self.dest_path:
|
||||
if field.startswith("album_seq"):
|
||||
album = pathlib.Path(dest_path).name
|
||||
album_info = _get_album_by_name(self.photo, album)
|
||||
else:
|
||||
album_info = _get_album_by_path(self.photo, dest_path)
|
||||
value = album_info.photo_index(self.photo) if album_info else None
|
||||
else:
|
||||
value = None
|
||||
if value is not None:
|
||||
try:
|
||||
with suppress(IndexError):
|
||||
start_id = field.split(".", 1)
|
||||
value = int(value) + int(start_id[1])
|
||||
except IndexError:
|
||||
pass
|
||||
value = format_str_value(value, subfield)
|
||||
elif field in PUNCTUATION:
|
||||
value = PUNCTUATION[field]
|
||||
elif field == "osxphotos_version":
|
||||
value = __version__
|
||||
elif field == "osxphotos_cmd_line":
|
||||
value = " ".join(sys.argv)
|
||||
else:
|
||||
# if here, didn't get a match
|
||||
raise ValueError(f"Unhandled template value: {field}")
|
||||
|
||||
# sanitize filename or directory name if needed
|
||||
if self.filename:
|
||||
value = sanitize_pathpart(value)
|
||||
elif self.dirname:
|
||||
@@ -1038,8 +1042,8 @@ class PhotoTemplate:
|
||||
field_value = None
|
||||
try:
|
||||
field_value = getattr(self, field_stem)
|
||||
except AttributeError:
|
||||
raise ValueError(f"Unknown path-like field: {field_stem}")
|
||||
except AttributeError as e:
|
||||
raise ValueError(f"Unknown path-like field: {field_stem}") from e
|
||||
|
||||
value = _get_pathlib_value(field, field_value, self.quote)
|
||||
|
||||
@@ -1083,14 +1087,14 @@ class PhotoTemplate:
|
||||
value = ["{" + values + "}"] if values else []
|
||||
elif filter_ == "parens":
|
||||
if values and type(values) == list:
|
||||
value = ["(" + v + ")" for v in values]
|
||||
value = [f"({v})" for v in values]
|
||||
else:
|
||||
value = ["(" + values + ")"] if values else []
|
||||
value = [f"({values})"] if values else []
|
||||
elif filter_ == "brackets":
|
||||
if values and type(values) == list:
|
||||
value = ["[" + v + "]" for v in values]
|
||||
value = [f"[{v}]" for v in values]
|
||||
else:
|
||||
value = ["[" + values + "]"] if values else []
|
||||
value = [f"[{values}]"] if values else []
|
||||
elif filter_ == "shell_quote":
|
||||
if values and type(values) == list:
|
||||
value = [shlex.quote(v) for v in values]
|
||||
@@ -1406,7 +1410,7 @@ def get_template_field_table():
|
||||
*TEMPLATE_SUBSTITUTIONS_MULTI_VALUED.items(),
|
||||
]:
|
||||
# replace '|' with '\|' to avoid markdown parsing issues (e.g. in {pipe} description)
|
||||
descr = descr.replace("'|'", "'\|'")
|
||||
descr = descr.replace("'|'", r"'\|'")
|
||||
template_table += f"\n|{subst}|{descr}|"
|
||||
return template_table
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ Photos tracks a tremendous amount of metadata associated with photos in the libr
|
||||
|
||||
`osxphotos export /path/to/export --exiftool`
|
||||
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with `--exiftool` to modify the metadata that is written by `exiftool`. For example, you can use the `--keyword-template` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchal keywords in the format used by Lightroom Classic:
|
||||
This will write basic metadata such as keywords, persons, and GPS location to the exported files. osxphotos includes several additional options that can be used in conjunction with `--exiftool` to modify the metadata that is written by `exiftool`. For example, you can use the `--keyword-template` option to specify custom keywords (again, via the osxphotos template system). For example, to use the folder and album a photo is in to create hierarchical keywords in the format used by Lightroom Classic:
|
||||
|
||||
osxphotos export /path/to/export --exiftool --keyword-template "{folder_album(>)}"
|
||||
│ │
|
||||
@@ -170,7 +170,7 @@ This will write basic metadata such as keywords, persons, and GPS location to th
|
||||
for joining the folders and albums. For example,
|
||||
if photo is in Folder1/Folder2/Album, (>) produces
|
||||
"Folder1>Folder2>Album" which some programs, such as
|
||||
Lightroom Classic, treat as hierarchal keywords
|
||||
Lightroom Classic, treat as hierarchical keywords
|
||||
|
||||
The above command will write all the regular metadata that `--exiftool` normally writes to the file upon export but will also add an additional keyword in the exported metadata in the form "Folder1>Folder2>Album". If you did not include the `(>)` in the template string (e.g. `{folder_album}`), folder_album would render in form "Folder1/Folder2/Album".
|
||||
|
||||
|
||||
@@ -22,10 +22,11 @@ pyobjc-framework-Vision>=7.3,<9.0
|
||||
pytimeparse2==1.4.0
|
||||
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.11.0
|
||||
rich>=11.2.0,<13.0.0
|
||||
tenacity>=8.0.1,<9.0.0
|
||||
textx>=2.3.0,<2.4.0
|
||||
toml>=0.10.2,<0.11.0
|
||||
wrapt>=1.13.3,<1.14.0
|
||||
wurlitzer>=2.1.0,<2.2.0
|
||||
xdg==5.1.1
|
||||
1
setup.py
1
setup.py
@@ -104,6 +104,7 @@ setup(
|
||||
"toml>=0.10.2,<0.11.0",
|
||||
"wrapt>=1.13.3,<1.14.0",
|
||||
"wurlitzer>=2.1.0,<3.0.0",
|
||||
"xdg==5.1.1",
|
||||
],
|
||||
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli_main"]},
|
||||
include_package_data=True,
|
||||
|
||||
@@ -80,19 +80,19 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# generate JSON files
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json")
|
||||
json_ = exporter._exiftool_json_sidecar()
|
||||
json_ = exporter.exiftool_json_sidecar()
|
||||
with open(sidecar, "w") as file:
|
||||
file.write(json_)
|
||||
|
||||
# no tag groups
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_no_tag_groups.json")
|
||||
json_ = exporter._exiftool_json_sidecar(tag_groups=False)
|
||||
json_ = exporter.exiftool_json_sidecar(tag_groups=False)
|
||||
with open(sidecar, "w") as file:
|
||||
file.write(json_)
|
||||
|
||||
# ignore_date_modified
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_ignore_date_modified.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(ignore_date_modified=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -100,7 +100,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# keyword_template
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_keyword_template.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=["{folder_album}"])
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -108,7 +108,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# persons_as_keywords
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_persons_as_keywords.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(use_persons_as_keywords=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
@@ -116,7 +116,7 @@ def generate_sidecars(dbname, uuid_dict):
|
||||
|
||||
# albums_as_keywords
|
||||
sidecar = str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_albums_as_keywords.json")
|
||||
json_ = exporter._exiftool_json_sidecar(
|
||||
json_ = exporter.exiftool_json_sidecar(
|
||||
ExportOptions(use_albums_as_keywords=True)
|
||||
)
|
||||
with open(sidecar, "w") as file:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" Test the command line interface (CLI) """
|
||||
import csv
|
||||
import datetime
|
||||
import glob
|
||||
import json
|
||||
@@ -9,7 +10,6 @@ import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from tempfile import TemporaryDirectory
|
||||
@@ -20,6 +20,7 @@ from osxmetadata import OSXMetaData, Tag
|
||||
|
||||
import osxphotos
|
||||
from osxphotos._constants import OSXPHOTOS_EXPORT_DB
|
||||
from osxphotos._version import __version__
|
||||
from osxphotos.cli import (
|
||||
about,
|
||||
albums,
|
||||
@@ -959,6 +960,18 @@ EXPORT_UNICODE_TITLE_FILENAMES = [
|
||||
"Frítest (3).jpg",
|
||||
]
|
||||
|
||||
# data for --report
|
||||
UUID_REPORT = [
|
||||
{
|
||||
"uuid": "4D521201-92AC-43E5-8F7C-59BC41C37A96",
|
||||
"filenames": ["IMG_1997.JPG", "IMG_1997.cr2"],
|
||||
},
|
||||
{
|
||||
"uuid": "7783E8E6-9CAC-40F3-BE22-81FB7051C266",
|
||||
"filenames": ["IMG_3092.heic", "IMG_3092_edited.jpeg"],
|
||||
},
|
||||
]
|
||||
|
||||
# data for --exif
|
||||
QUERY_EXIF_DATA = [("EXIF:Make", "FUJIFILM", ["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])]
|
||||
QUERY_EXIF_DATA_CASE_INSENSITIVE = [
|
||||
@@ -5472,13 +5485,239 @@ def test_export_report():
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
# test report creation
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V", "--report", "report.csv"],
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
"report.csv",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Writing export report" in result.output
|
||||
assert "Wrote export report" in result.output
|
||||
assert os.path.exists("report.csv")
|
||||
with open("report.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[0]["filenames"])
|
||||
|
||||
# test report gets overwritten
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[1]["uuid"],
|
||||
"--report",
|
||||
"report.csv",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
with open("report.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[1]["filenames"])
|
||||
|
||||
# test report with --append
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
"report.csv",
|
||||
"--overwrite",
|
||||
"--append",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
with open("report.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(
|
||||
UUID_REPORT[0]["filenames"] + UUID_REPORT[1]["filenames"]
|
||||
)
|
||||
|
||||
|
||||
def test_export_report_json():
|
||||
"""test export with --report option for JSON report"""
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
# test report creation
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
"report.json",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Wrote export report" in result.output
|
||||
assert os.path.exists("report.json")
|
||||
with open("report.json", "r") as f:
|
||||
rows = json.load(f)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[0]["filenames"])
|
||||
|
||||
# test report gets overwritten
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[1]["uuid"],
|
||||
"--report",
|
||||
"report.json",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
with open("report.json", "r") as f:
|
||||
rows = json.load(f)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[1]["filenames"])
|
||||
|
||||
# test report with --append
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
"report.json",
|
||||
"--overwrite",
|
||||
"--append",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
with open("report.json", "r") as f:
|
||||
rows = json.load(f)
|
||||
filenames = [str(pathlib.Path(row["filename"]).name) for row in rows]
|
||||
assert sorted(filenames) == sorted(
|
||||
UUID_REPORT[0]["filenames"] + UUID_REPORT[1]["filenames"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("report_file", ["report.db", "report.sqlite"])
|
||||
def test_export_report_sqlite(report_file):
|
||||
"""test export with --report option with sqlite report"""
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
# test report creation
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
report_file,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Wrote export report" in result.output
|
||||
assert os.path.exists(report_file)
|
||||
conn = sqlite3.connect(report_file)
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT filename FROM report")
|
||||
filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[0]["filenames"])
|
||||
|
||||
# test report gets overwritten
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[1]["uuid"],
|
||||
"--report",
|
||||
report_file,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
conn = sqlite3.connect(report_file)
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT filename FROM report")
|
||||
filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()]
|
||||
assert sorted(filenames) == sorted(UUID_REPORT[1]["filenames"])
|
||||
|
||||
# test report with --append
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--uuid",
|
||||
UUID_REPORT[0]["uuid"],
|
||||
"--report",
|
||||
report_file,
|
||||
"--overwrite",
|
||||
"--append",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
conn = sqlite3.connect(report_file)
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT filename FROM report")
|
||||
filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()]
|
||||
assert sorted(filenames) == sorted(
|
||||
UUID_REPORT[0]["filenames"] + UUID_REPORT[1]["filenames"]
|
||||
)
|
||||
|
||||
|
||||
def test_export_report_template():
|
||||
"""test export with --report option with a template for report name"""
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, CLI_PHOTOS_DB),
|
||||
".",
|
||||
"-V",
|
||||
"--report",
|
||||
"report_{osxphotos_version}.csv",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Wrote export report" in result.output
|
||||
assert os.path.exists(f"report_{__version__}.csv")
|
||||
|
||||
|
||||
def test_export_report_not_a_file():
|
||||
@@ -5492,7 +5731,7 @@ def test_export_report_not_a_file():
|
||||
export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V", "--report", "."]
|
||||
)
|
||||
assert result.exit_code != 0
|
||||
assert "report is a directory, must be file name" in result.output
|
||||
assert "is a directory, must be file name" in result.output
|
||||
|
||||
|
||||
def test_export_as_hardlink_download_missing():
|
||||
@@ -7794,6 +8033,7 @@ def test_export_limit():
|
||||
assert result.exit_code == 0
|
||||
assert "limit: 0/20 exported" in result.output
|
||||
|
||||
|
||||
def test_export_no_keyword():
|
||||
"""test export --no-keyword"""
|
||||
|
||||
@@ -7812,4 +8052,4 @@ def test_export_no_keyword():
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Exporting 11" in result.output
|
||||
assert "Exporting 11" in result.output
|
||||
|
||||
266
tests/test_cli_exiftool.py
Normal file
266
tests/test_cli_exiftool.py
Normal file
@@ -0,0 +1,266 @@
|
||||
"""Tests for `osxphotos exiftool` command."""
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from osxphotos.cli.exiftool_cli import exiftool
|
||||
from osxphotos.cli.export import export
|
||||
from osxphotos.exiftool import ExifTool, get_exiftool_path
|
||||
|
||||
from .test_cli import CLI_EXIFTOOL, PHOTOS_DB_15_7
|
||||
|
||||
# determine if exiftool installed so exiftool tests can be skipped
|
||||
try:
|
||||
exiftool_path = get_exiftool_path()
|
||||
except FileNotFoundError:
|
||||
exiftool_path = None
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool():
|
||||
"""Test osxphotos exiftool"""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
files = glob.glob("*")
|
||||
assert sorted(files) == sorted(
|
||||
[CLI_EXIFTOOL[uuid]["File:FileName"] for uuid in CLI_EXIFTOOL]
|
||||
)
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
["--db", os.path.join(cwd, PHOTOS_DB_15_7), "-V", "--db-config", temp_dir],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict()
|
||||
for key in CLI_EXIFTOOL[uuid]:
|
||||
if type(exif[key]) == list:
|
||||
assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key])
|
||||
else:
|
||||
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_album_keyword():
|
||||
"""Test osxphotos exiftool with --album-template."""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--album",
|
||||
"Pumpkin Farm",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
files = glob.glob("*")
|
||||
assert len(files) == 3
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"-V",
|
||||
"--db-config",
|
||||
"--report",
|
||||
"exiftool.json",
|
||||
"--album-keyword",
|
||||
temp_dir,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
report = json.load(open("exiftool.json", "r"))
|
||||
assert len(report) == 3
|
||||
|
||||
# verify exiftool metadata was updated
|
||||
for file in report:
|
||||
exif = ExifTool(file["filename"]).asdict()
|
||||
assert "Pumpkin Farm" in exif["IPTC:Keywords"]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
"--album",
|
||||
"Pumpkin Farm",
|
||||
"--album-keyword",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: 3" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_keyword_template():
|
||||
"""Test osxphotos exiftool with --keyword-template."""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"-V",
|
||||
"--db-config",
|
||||
"--keyword-template",
|
||||
"FOO",
|
||||
temp_dir,
|
||||
"--report",
|
||||
"exiftool.json",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
report = json.load(open("exiftool.json", "r"))
|
||||
for file in report:
|
||||
exif = ExifTool(file["filename"]).asdict()
|
||||
assert "FOO" in exif["IPTC:Keywords"]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--keyword-template",
|
||||
"FOO",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed")
|
||||
def test_export_exiftool_load_config():
|
||||
"""Test osxphotos exiftool with --load-config"""
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
|
||||
with runner.isolated_filesystem() as temp_dir:
|
||||
uuid_option = []
|
||||
for uuid in CLI_EXIFTOOL:
|
||||
uuid_option.extend(("--uuid", uuid))
|
||||
|
||||
# first, export without --exiftool
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--save-config",
|
||||
"config.toml",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
# now, run exiftool command to update exiftool metadata
|
||||
result = runner.invoke(
|
||||
exiftool,
|
||||
["-V", "--load-config", "config.toml", temp_dir],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
exif = ExifTool(CLI_EXIFTOOL[uuid]["File:FileName"]).asdict()
|
||||
for key in CLI_EXIFTOOL[uuid]:
|
||||
if type(exif[key]) == list:
|
||||
assert sorted(exif[key]) == sorted(CLI_EXIFTOOL[uuid][key])
|
||||
else:
|
||||
assert exif[key] == CLI_EXIFTOOL[uuid][key]
|
||||
|
||||
# now, export with --exiftool --update, no files should be updated
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
temp_dir,
|
||||
"-V",
|
||||
"--exiftool",
|
||||
"--update",
|
||||
*uuid_option,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert f"exported: 0, updated: 0, skipped: {len(CLI_EXIFTOOL)}" in result.output
|
||||
@@ -25,6 +25,7 @@ if OS_VER == "15":
|
||||
from tests.config_timewarp_catalina import CATALINA_PHOTOS_5 as TEST_DATA
|
||||
|
||||
else:
|
||||
pytest.skip(allow_module_level=True)
|
||||
TEST_DATA = {}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
""" test ConfigOptions class """
|
||||
|
||||
import pathlib
|
||||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
import toml
|
||||
|
||||
@@ -43,6 +45,15 @@ def test_write_to_file_load_from_file(tmpdir):
|
||||
assert cfg2.bar
|
||||
|
||||
|
||||
def test_load_from_str(tmpdir):
|
||||
cfg = ConfigOptions("test", VARS)
|
||||
cfg.bar = True
|
||||
cfg_str = cfg.write_to_str()
|
||||
cfg2 = ConfigOptions("test", VARS).load_from_str(cfg_str)
|
||||
assert cfg2.foo == "bar"
|
||||
assert cfg2.bar
|
||||
|
||||
|
||||
def test_load_from_file_error(tmpdir):
|
||||
cfg_file = pathlib.Path(str(tmpdir)) / "test.toml"
|
||||
cfg = ConfigOptions("test", VARS)
|
||||
|
||||
@@ -404,7 +404,7 @@ def test_exiftool_json_sidecar(photosdb):
|
||||
with open(str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json"), "r") as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar()
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar()
|
||||
json_got = json.loads(json_got)[0]
|
||||
|
||||
assert json_got == json_expected
|
||||
@@ -420,7 +420,7 @@ def test_exiftool_json_sidecar_ignore_date_modified(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(ignore_date_modified=True)
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -453,7 +453,7 @@ def test_exiftool_json_sidecar_keyword_template_long(capsys, photosdb):
|
||||
|
||||
long_str = "x" * (_MAX_IPTC_KEYWORD_LEN + 1)
|
||||
photos[0]._verbose = print
|
||||
json_got = PhotoExporter(photos[0])._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photos[0]).exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=[long_str])
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -479,7 +479,7 @@ def test_exiftool_json_sidecar_keyword_template(photosdb):
|
||||
str(pathlib.Path(SIDECAR_DIR) / f"{uuid}_keyword_template.json"), "r"
|
||||
) as fp:
|
||||
json_expected = json.load(fp)
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(keyword_template=["{folder_album}"])
|
||||
)
|
||||
json_got = json.loads(json_got)
|
||||
@@ -497,7 +497,7 @@ def test_exiftool_json_sidecar_use_persons_keyword(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(use_persons_as_keywords=True)
|
||||
)
|
||||
json_got = json.loads(json_got)[0]
|
||||
@@ -515,7 +515,7 @@ def test_exiftool_json_sidecar_use_albums_keywords(photosdb):
|
||||
) as fp:
|
||||
json_expected = json.load(fp)
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(
|
||||
ExportOptions(use_albums_as_keywords=True)
|
||||
)
|
||||
json_got = json.loads(json_got)
|
||||
@@ -530,7 +530,7 @@ def test_exiftool_sidecar(photosdb):
|
||||
with open(pathlib.Path(SIDECAR_DIR) / f"{uuid}_no_tag_groups.json", "r") as fp:
|
||||
json_expected = fp.read()
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar(tag_groups=False)
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar(tag_groups=False)
|
||||
|
||||
assert json_got == json_expected
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ def test_exiftool_json_sidecar(photosdb):
|
||||
with open(str(pathlib.Path(SIDECAR_DIR) / f"{uuid}.json"), "r") as fp:
|
||||
json_expected = json.load(fp)[0]
|
||||
|
||||
json_got = PhotoExporter(photo)._exiftool_json_sidecar()
|
||||
json_got = PhotoExporter(photo).exiftool_json_sidecar()
|
||||
json_got = json.loads(json_got)[0]
|
||||
|
||||
assert json_got == json_expected
|
||||
|
||||
Reference in New Issue
Block a user