Compare commits

..

16 Commits

Author SHA1 Message Date
Rhet Turnbull
8e3578e29b Release 0.50.13 (#756) 2022-08-13 08:50:16 -07:00
Rhet Turnbull
38a5998063 Feature orphans (#755)
* Initial implementation of orphan command

* Implemented orphans command, #84
2022-08-13 08:43:02 -07:00
Rhet Turnbull
2103d8bcad Added bad_photos example [skip ci] 2022-08-11 22:06:33 -07:00
Rhet Turnbull
26a9028497 Add PhotosAlbumPhotosKit to __all__ 2022-08-10 21:37:33 -06:00
Rhet Turnbull
e41f89480a Add PhotosAlbum to osxphotos __all__ 2022-08-10 20:53:07 -06:00
Rhet Turnbull
db3c37fb5b Hot fix for 749 (#750) 2022-08-08 06:26:08 -07:00
Rhet Turnbull
3d83e184b8 Added strip_live.py example (#747) 2022-08-06 21:19:51 -07:00
Rhet Turnbull
28e03ee86a Added reddit badge for r/osxphotos (#746) 2022-08-02 07:11:55 -07:00
Rhet Turnbull
fac45c7141 Updated CHANGELOG.md [skip ci] 2022-08-01 21:00:44 -07:00
Rhet Turnbull
f4154e8691 Feature not reference 738 (#744)
* Added --not-reference, 738

* Release files for #738
2022-07-28 06:24:58 -07:00
allcontributors[bot]
b4c4d9fb90 docs: add nullpointerninja as a contributor for ideas (#743)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-27 09:46:19 -07:00
allcontributors[bot]
402bbbdbae docs: add franzone as a contributor for bug (#742)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-27 09:44:25 -07:00
Rhet Turnbull
f7771909d6 Updated docs for v0.58.10 [skip-ci] (#741) 2022-07-27 08:25:06 -07:00
Rhet Turnbull
76625b9e84 Feature add keep 730 (#740)
* Implemented #730, --keep

* Implemented #739, fixed --keep to accept relative paths

* Updated to macos-latest

Ref: https://github.com/actions/virtual-environments/issues/5583

* Release files for #730
2022-07-27 08:14:00 -07:00
allcontributors[bot]
fb4329e0ed docs: add Se7enair as a contributor for ideas (#737)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-07-24 04:50:20 -07:00
Rhet Turnbull
2b52460de7 Updated CHANGELOG.md [skip ci] 2022-07-23 18:27:16 -07:00
45 changed files with 1323 additions and 314 deletions

View File

@@ -333,7 +333,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/62975432?v=4",
"profile": "https://github.com/nullpointerninja",
"contributions": [
"bug"
"bug",
"ideas"
]
},
{
@@ -344,6 +345,24 @@
"contributions": [
"ideas"
]
},
{
"login": "Se7enair",
"name": "Christoph",
"avatar_url": "https://avatars.githubusercontent.com/u/1680106?v=4",
"profile": "https://github.com/Se7enair",
"contributions": [
"ideas"
]
},
{
"login": "franzone",
"name": "franzone",
"avatar_url": "https://avatars.githubusercontent.com/u/900684?v=4",
"profile": "http://www.franzone.com",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,

View File

@@ -9,7 +9,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
os: [macos-10.15]
os: [macos-latest]
python-version: ['3.8', '3.9', '3.10']
steps:

View File

@@ -2005,7 +2005,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.50.9'|
|{osxphotos_version}|The osxphotos version, e.g. '0.50.13'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@@ -4,6 +4,29 @@ 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.50.11](https://github.com/RhetTbull/osxphotos/compare/v0.50.10...v0.50.11)
> 28 July 2022
- Feature not reference 738 [`#744`](https://github.com/RhetTbull/osxphotos/pull/744)
- docs: add nullpointerninja as a contributor for ideas [`#743`](https://github.com/RhetTbull/osxphotos/pull/743)
- docs: add franzone as a contributor for bug [`#742`](https://github.com/RhetTbull/osxphotos/pull/742)
#### [v0.50.10](https://github.com/RhetTbull/osxphotos/compare/v0.50.9...v0.50.10)
> 27 July 2022
- Updated docs for v0.58.10 [skip-ci] [`#741`](https://github.com/RhetTbull/osxphotos/pull/741)
- Feature add keep 730 [`#740`](https://github.com/RhetTbull/osxphotos/pull/740)
- docs: add Se7enair as a contributor for ideas [`#737`](https://github.com/RhetTbull/osxphotos/pull/737)
#### [v0.50.9](https://github.com/RhetTbull/osxphotos/compare/v0.50.8...v0.50.9)
> 23 July 2022
- Release files for #732, add --favorite-rating [`f8f9bd7`](https://github.com/RhetTbull/osxphotos/commit/f8f9bd7b933c077528649560d692ceb22d254768)
- Implemented --favorite-rating, #732 [`5d33dcd`](https://github.com/RhetTbull/osxphotos/commit/5d33dcdcc3af1ca9dfa11b7be2ab51f6906d9e61)
#### [v0.50.8](https://github.com/RhetTbull/osxphotos/compare/v0.50.7...v0.50.8)
> 23 July 2022

275
README.md
View File

@@ -5,8 +5,9 @@
[![tests](https://github.com/RhetTbull/osxphotos/workflows/Tests/badge.svg)](https://github.com/RhetTbull/osxphotos/workflows/Tests/badge.svg)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/osxphotos)
[![Downloads](https://static.pepy.tech/personalized-badge/osxphotos?period=month&units=international_system&left_color=black&right_color=brightgreen&left_text=downloads/month)](https://pepy.tech/project/osxphotos)
[![subreddit](https://img.shields.io/reddit/subreddit-subscribers/osxphotos?style=social)](https://www.reddit.com/r/osxphotos/)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-36-orange.svg?style=flat)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-38-orange.svg?style=flat)](#contributors)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
@@ -774,6 +775,9 @@ Options:
--is-reference Search for photos that were imported as
referenced files (not copied into Photos
library).
--not-reference Search for photos that are not references,
that is, they were copied into the Photos
library and are managed by Photos.
--in-album Search for photos that are in one or more
albums.
--not-in-album Search for photos that are not in any albums.
@@ -1195,6 +1199,29 @@ Options:
you intend before using --cleanup. Use --dry-
run with --cleanup first if you're not
certain.
--keep KEEP_PATH When used with --cleanup, prevents file or
directory KEEP_PATH from being deleted when
cleanup is run. Use this if there are files in
the export directory that you don't want to be
deleted when --cleanup is run. KEEP_PATH may
be a file path, e.g.
'/Volumes/Photos/keep.jpg', or a file path and
wild card, e.g. '/Volumes/Photos/*.txt', or a
directory, e.g. '/Volumes/Photos/KeepMe'.
KEEP_PATH may be an absolute path or a
relative path. If it is relative, it must be
relative to the export destination. For
example if export destination is
`/Volumes/Photos` and you want to keep all
`.txt` files, you can specify `--keep
"/Volumes/Photos/*.txt"` or `--keep "*.txt"`.
If wild card is used, KEEP_PATH must be
enclosed in quotes to prevent the shell from
expanding the wildcard, e.g. `--keep
"/Volumes/Photos/*.txt"`. If KEEP_PATH is a
directory, all files and directories contained
in KEEP_PATH will be kept. --keep may be
repeated to keep additional files/directories.
--add-exported-to-album ALBUM Add all exported photos to album ALBUM in
Photos. Album ALBUM will be created if it
doesn't exist. All exported photos will be
@@ -1926,7 +1953,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.50.9'
{osxphotos_version} The osxphotos version, e.g. '0.50.13'
{osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified
@@ -2403,7 +2430,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.50.9'|
|{osxphotos_version}|The osxphotos version, e.g. '0.50.13'|
|{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|
@@ -2428,244 +2455,6 @@ The following template field substitutions are availabe for use the templating s
|{function}|Execute a python function from an external file and use return value as template substitution. Use in format: {function:file.py::function_name} where 'file.py' is the name of the python file and 'function_name' is the name of the function to call. The function will be passed the PhotoInfo object for the photo. See https://github.com/RhetTbull/osxphotos/blob/master/examples/template_function.py for an example of how to implement a template function.|
<!-- OSXPHOTOS-TEMPLATE-TABLE:END -->
### <a name="exiftoolExifTool">ExifTool</a>
osxphotos includes its own `exiftool` library that can be accessed via `osxphotos.exiftool`:
```python
>>> from osxphotos.exiftool import ExifTool
>>> exiftool = ExifTool("/Users/rhet/Downloads/test.jpeg")
>>> exifdict = exiftool.asdict()
>>> exifdict["EXIF:Make"]
'Canon'
>>> exiftool.setvalue("IPTC:Keywords","Keyword1")
True
>>> exiftool.asdict()["IPTC:Keywords"]
'Keyword1'
>>> exiftool.addvalues("IPTC:Keywords","Keyword2","Keyword3")
True
>>> exiftool.asdict()["IPTC:Keywords"]
['Keyword1', 'Keyword2', 'Keyword3']
```
`ExifTool(filepath, exiftool=None)`
* `filepath`: str, path to photo
* `exiftool`: str, optional path to `exiftool`; if not provided, will look for `exiftool` in the system path
#### ExifTool methods
* `asdict(tag_groups=True)`: returns all EXIF metadata found in the file as a dictionary in following form (Note: this shows just a subset of available metadata). See [exiftool](https://exiftool.org/) documentation to understand which metadata keys are available. If `tag_groups` is True (default) dict keys are in form "GROUP:TAG", e.g. "IPTC:Keywords". If `tag_groups` is False, dict keys do not have group names, e.g. "Keywords".
```python
{'Composite:Aperture': 2.2,
'Composite:GPSPosition': '-34.9188916666667 138.596861111111',
'Composite:ImageSize': '2754 2754',
'EXIF:CreateDate': '2017:06:20 17:18:56',
'EXIF:LensMake': 'Apple',
'EXIF:LensModel': 'iPhone 6s back camera 4.15mm f/2.2',
'EXIF:Make': 'Apple',
'XMP:Title': 'Elder Park',
}
```
* `json()`: returns same information as `asdict()` but as a serialized JSON string.
* `setvalue(tag, value)`: write to the EXIF data in the photo file. To delete a tag, use setvalue with value = `None`. For example:
```python
photo.exiftool.setvalue("XMP:Title", "Title of photo")
```
* `addvalues(tag, *values)`: Add one or more value(s) to tag. For a tag that accepts multiple values, like "IPTC:Keywords", this will add the values as additional list values. However, for tags which are not usually lists, such as "EXIF:ISO" this will literally add the new value to the old value which is probably not the desired effect. Be sure you understand the behavior of the individual tag before using this. For example:
```python
photo.exiftool.addvalues("IPTC:Keywords", "vacation", "beach")
```
osxphotos.exiftool also provides an `ExifToolCaching` class which caches all metadata after the first call to `exiftool`. This can significantly speed up repeated access to the metadata but should only be used if you do not intend to modify the file's metadata.
[`PhotoInfo.exiftool`](#exiftool) returns an `ExifToolCaching` instance for the original image in the Photos library.
#### Implementation Note
`ExifTool()` runs `exiftool` as a subprocess using the `-stay_open True` flag to keep the process running in the background. The subprocess will be cleaned up when your main script terminates. `ExifTool()` uses a singleton pattern to ensure that only one instance of `exiftool` is created. Multiple instances of `ExifTool()` will all use the same `exiftool` subprocess.
### <a name="photoexporter">PhotoExporter</a>
[PhotoInfo.export()](#photoinfo) provides a simple method to export a photo. This method actually calls `PhotoExporter.export()` to do the export. `PhotoExporter` provides many more options to configure the export and report results and this is what the osxphotos command line export tools uses.
#### `export(dest, filename=None, options: Optional[ExportOptions]=None) -> ExportResults`
Export a photo.
Args:
* dest: must be valid destination path or exception raised
* filename: (optional): name of exported picture; if not provided, will use current filename
* options (ExportOptions): optional ExportOptions instance
Returns: ExportResults instance
*Note*: to use dry run mode, you must set options.dry_run=True and also pass in memory version of export_db, and no-op fileutil (e.g. `ExportDBInMemory` and `FileUtilNoOp`) in options.export_db and options.fileutil respectively.
#### `ExportOptions`
Options class for exporting photos with `export`
Attributes:
* convert_to_jpeg (bool): if True, converts non-jpeg images to jpeg
* description_template (str): optional template string that will be rendered for use as photo description
* download_missing: (bool, default=False): if True will attempt to export photo via applescript interaction with Photos if missing (see also use_photokit, use_photos_export)
* dry_run: (bool, default=False): set to True to run in "dry run" mode
* edited: (bool, default=False): if True will export the edited version of the photo otherwise exports the original version
* exiftool_flags (list of str): optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"]
* exiftool: (bool, default = False): if True, will use exiftool to write metadata to export file
* export_as_hardlink: (bool, default=False): if True, will hardlink files instead of copying them
* export_db: (ExportDB): instance of a class that conforms to ExportDB with methods for getting/setting data related to exported files to compare update state
* fileutil: (FileUtilABC): class that conforms to FileUtilABC with various file utilities
* ignore_date_modified (bool): for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
* ignore_signature (bool, default=False): ignore file signature when used with update (look only at filename)
* increment (bool, default=True): if True, will increment file name until a non-existant name is found if overwrite=False and increment=False, export will fail if destination file already exists
* jpeg_ext (str): if set, will use this value for extension on jpegs converted to jpeg with convert_to_jpeg; if not set, uses jpeg; do not include the leading "."
* jpeg_quality (float in range 0.0 <= jpeg_quality <= 1.0): a value of 1.0 specifies use best quality, a value of 0.0 specifies use maximum compression.
* keyword_template (list of str): list of template strings that will be rendered as used as keywords
* live_photo (bool, default=False): if True, will also export the associated .mov for live photos
* location (bool): if True, include location in exported metadata
* merge_exif_keywords (bool): if True, merged keywords found in file's exif data (requires exiftool)
* merge_exif_persons (bool): if True, merged persons found in file's exif data (requires exiftool)
* overwrite (bool, default=False): if True will overwrite files if they already exist
* persons (bool): if True, include persons in exported metadata
* preview_suffix (str): optional string to append to end of filename for preview images
* preview (bool): if True, also exports preview image
* raw_photo (bool, default=False): if True, will also export the associated RAW photo
* render_options (RenderOptions): optional osxphotos.phototemplate.RenderOptions instance to specify options for rendering templates
* replace_keywords (bool): if True, keyword_template replaces any keywords, otherwise it's additive
* sidecar_drop_ext (bool, default=False): if True, drops the photo's extension from sidecar filename (e.g. 'IMG_1234.json' instead of 'IMG_1234.JPG.json')
* sidecar: bit field (int): set to one or more of SIDECAR_XMP, SIDECAR_JSON, SIDECAR_EXIFTOOL
* SIDECAR_JSON: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; includes exiftool tag group names (e.g. `exiftool -G -j`)
* SIDECAR_EXIFTOOL: if set will write a json sidecar with data in format readable by exiftool sidecar filename will be dest/filename.json; does not include exiftool tag group names (e.g. `exiftool -j`)
* SIDECAR_XMP: if set will write an XMP sidecar with IPTC data sidecar filename will be dest/filename.xmp
* strip (bool): if True, strip whitespace from rendered templates
* timeout (int, default=120): timeout in seconds used with use_photos_export
* touch_file (bool, default=False): if True, sets file's modification time upon photo date
* update (bool, default=False): if True export will run in update mode, that is, it will not export the photo if the current version already exists in the destination
* use_albums_as_keywords (bool, default = False): if True, will include album names in keywords when exporting metadata with exiftool or sidecar
* use_persons_as_keywords (bool, default = False): if True, will include person names in keywords when exporting metadata with exiftool or sidecar
* use_photos_export (bool, default=False): if True will attempt to export photo via applescript interaction with Photos even if not missing (see also use_photokit, download_missing)
* use_photokit (bool, default=False): if True, will use photokit to export photos when use_photos_export is True
* verbose (Callable): optional callable function to use for printing verbose text during processing; if None (default), does not print output.
* tmpfile (str): optional path to use for temporary files
#### `ExportResults`
`PhotoExporter().export()` returns an instance of this class.
`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
* skipped: list of skipped files when used with update=True
* exif_updated: list of updated files when used with update=True and exiftool
* touched: list of files touched during export (e.g. file date/time updated with touch_file=True)
* to_touch: Reserved for internal use of export
* converted_to_jpeg: list of files converted to jpeg when convert_to_jpeg=True
* sidecar_json_written: list of JSON sidecars written
* sidecar_json_skipped: list of JSON sidecars skipped when update=True
* sidecar_exiftool_written: list of exiftool sidecars written
* sidecar_exiftool_skipped: list of exiftool sidecars skipped when update=True
* sidecar_xmp_written: list of XMP sidecars written
* sidecar_xmp_skipped: list of XMP sidecars skipped when update=True
* missing: list of missing files
* error: list of tuples containing (filename, error) if error generated during export
* exiftool_warning: list of warnings generated by exiftool during export
* exiftool_error: list of errors generated by exiftool during export
* xattr_written: list of files with extended attributes written during export
* xattr_skipped: list of files where extended attributes were skipped when update=True
* metadata_changed: list of files where metadata changed since last export
* deleted_files: reserved for use by osxphotos CLI
* deleted_directories: reserved for use by osxphotos CLI
* exported_album: reserved for use by osxphotos CLI
* skipped_album: reserved for use by osxphotos CLI
* missing_album: reserved for use by osxphotos CLI
### <a name="textdetection">Text Detection</a>
The [PhotoInfo.detected_text()](#detected_text_method) and the `{detected_text}` template will perform text detection on the photos in your library. Text detection is a slow process so to avoid unnecessary re-processing of photos, osxphotos will cache the results of the text detection process as an extended attribute on the photo image file. Extended attributes do not modify the actual file. The extended attribute is named `osxphotos.metadata:detected_text` and can be viewed using the built-in [xattr](https://ss64.com/osx/xattr.html) command or my [osxmetadata](https://github.com/RhetTbull/osxmetadata) tool. If you want to remove the cached attribute, you can do so with osxmetadata as follows:
`osxmetadata --clear osxphotos.metadata:detected_text --walk ~/Pictures/Photos\ Library.photoslibrary/`
### Utility Functions
The following functions are located in osxphotos.utils
#### `get_system_library_path()`
**MacOS 10.15 Only** Returns path to System Photo Library as string. On MacOS version < 10.15, returns None.
#### `get_last_library_path()`
Returns path to last opened Photo Library as string.
#### `list_photo_libraries()`
Returns list of Photos libraries found on the system. **Note**: On MacOS 10.15, this appears to list all libraries. On older systems, it may not find some libraries if they are not located in ~/Pictures. Provided for convenience but do not rely on this to find all libraries on the system.
## Examples
```python
import osxphotos
def main():
photosdb = osxphotos.PhotosDB("/Users/smith/Pictures/Photos Library.photoslibrary")
print(f"db file = {photosdb.db_path}")
print(f"db version = {photosdb.db_version}")
print(photosdb.keywords)
print(photosdb.persons)
print(photosdb.album_names)
print(photosdb.keywords_as_dict)
print(photosdb.persons_as_dict)
print(photosdb.albums_as_dict)
# find all photos with Keyword = Kids and containing person Katie
photos = photosdb.photos(keywords=["Kids"], persons=["Katie"])
print(f"found {len(photos)} photos")
# find all photos that include Katie but do not contain the keyword wedding
photos = [
p
for p in photosdb.photos(persons=["Katie"])
if p not in photosdb.photos(keywords=["wedding"])
]
# get all photos in the database
photos = photosdb.photos()
for p in photos:
print(
p.uuid,
p.filename,
p.date,
p.description,
p.title,
p.keywords,
p.albums,
p.persons,
p.path,
p.ismissing,
p.hasadjustments,
)
if __name__ == "__main__":
main()
```
## Related Projects
* [rhettbull/exif2findertags](https://github.com/RhetTbull/exif2findertags): Read EXIF metadata from image and video files and convert it to macOS Finder tags and/or Finder comments and other extended attributes.
@@ -2734,10 +2523,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://github.com/neebah"><img src="https://avatars.githubusercontent.com/u/71442026?v=4?s=75" width="75px;" alt=""/><br /><sub><b>neebah</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aneebah" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ahti123"><img src="https://avatars.githubusercontent.com/u/22232632?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Ahti Liin</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=ahti123" title="Code">💻</a> <a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aahti123" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xwu64"><img src="https://avatars.githubusercontent.com/u/10580396?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Xiaoliang Wu</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=xwu64" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nullpointerninja"><img src="https://avatars.githubusercontent.com/u/62975432?v=4?s=75" width="75px;" alt=""/><br /><sub><b>nullpointerninja</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anullpointerninja" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/nullpointerninja"><img src="https://avatars.githubusercontent.com/u/62975432?v=4?s=75" width="75px;" alt=""/><br /><sub><b>nullpointerninja</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anullpointerninja" title="Bug reports">🐛</a> <a href="#ideas-nullpointerninja" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/infused-kim"><img src="https://avatars.githubusercontent.com/u/7404004?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Kim</b></sub></a><br /><a href="#ideas-infused-kim" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/Se7enair"><img src="https://avatars.githubusercontent.com/u/1680106?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Christoph</b></sub></a><br /><a href="#ideas-Se7enair" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="http://www.franzone.com"><img src="https://avatars.githubusercontent.com/u/900684?v=4?s=75" width="75px;" alt=""/><br /><sub><b>franzone</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Afranzone" title="Bug reports">🐛</a></td>
</tr>
</table>

View File

@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 9565e5b4ec4fa43c83e599eb4b97045f
config: fb04ea7e5d453b9c94599f1736a60474
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>Overview: module code - osxphotos 0.50.9 documentation</title>
<title>Overview: module code - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -205,6 +205,7 @@
<li><a href="osxphotos/personinfo.html">osxphotos.personinfo</a></li>
<li><a href="osxphotos/photoexporter.html">osxphotos.photoexporter</a></li>
<li><a href="osxphotos/photoinfo.html">osxphotos.photoinfo</a></li>
<li><a href="osxphotos/photosalbum.html">osxphotos.photosalbum</a></li>
<li><a href="osxphotos/photosdb/_photosdb_process_comments.html">osxphotos.photosdb._photosdb_process_comments</a></li>
<li><a href="osxphotos/photosdb/photosdb.html">osxphotos.photosdb.photosdb</a></li>
<li><a href="osxphotos/phototemplate.html">osxphotos.phototemplate</a></li>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.exiftool - osxphotos 0.50.5 documentation</title>
<title>osxphotos.exiftool - osxphotos 0.50.13 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.50.5 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.5 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photoexporter - osxphotos 0.50.9 documentation</title>
<title>osxphotos.photoexporter - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -0,0 +1,320 @@
<!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" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosalbum - osxphotos 0.50.13 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" />
<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.50.13 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">
<span class="sidebar-brand-text">osxphotos 0.50.13 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>
</div>
</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.photosalbum</h1><div class="highlight"><pre>
<span></span><span class="sd">""" PhotosAlbum class to create an album in default Photos library and add photos to it """</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">photoscript</span>
<span class="kn">from</span> <span class="nn">more_itertools</span> <span class="kn">import</span> <span class="n">chunked</span>
<span class="kn">from</span> <span class="nn">photoscript</span> <span class="kn">import</span> <span class="n">Photo</span><span class="p">,</span> <span class="n">PhotosLibrary</span>
<span class="kn">from</span> <span class="nn">.photoinfo</span> <span class="kn">import</span> <span class="n">PhotoInfo</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">noop</span><span class="p">,</span> <span class="n">pluralize</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"PhotosAlbum"</span><span class="p">,</span> <span class="s2">"PhotosAlbumPhotoScript"</span><span class="p">]</span>
<div class="viewcode-block" id="PhotosAlbum"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbum">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbum</span><span class="p">:</span>
<span class="sd">"""Add osxphotos.photoinfo.PhotoInfo objects to album"""</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="bp">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">album</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating Photos album '</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span> <span class="o">=</span> <span class="n">album</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">PhotoInfo</span><span class="p">):</span>
<span class="n">photo_</span> <span class="o">=</span> <span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo_</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="n">photo</span><span class="o">.</span><span class="n">original_filename</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">PhotoInfo</span><span class="p">]):</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photo_list</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">photos</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">photoscript</span><span class="o">.</span><span class="n">Photo</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Error creating Photo object for photo </span><span class="si">{</span><span class="n">p</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photos</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="n">photo_len</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">photos</span><span class="p">()</span></div>
<div class="viewcode-block" id="PhotosAlbumPhotoScript"><a class="viewcode-back" href="../../reference.html#osxphotos.PhotosAlbumPhotoScript">[docs]</a><span class="k">class</span> <span class="nc">PhotosAlbumPhotoScript</span><span class="p">:</span>
<span class="sd">"""Add photoscript.Photo objects to album"""</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">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">verbose</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">callable</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span> <span class="ow">or</span> <span class="n">noop</span>
<span class="bp">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">PhotosLibrary</span><span class="p">()</span>
<span class="n">album</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">library</span><span class="o">.</span><span class="n">album</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">if</span> <span class="n">album</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Creating Photos album '</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
<span class="n">album</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">library</span><span class="o">.</span><span class="n">create_album</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span> <span class="o">=</span> <span class="n">album</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo</span><span class="p">:</span> <span class="n">Photo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">([</span><span class="n">photo</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="n">photo</span><span class="o">.</span><span class="n">filename</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">photo</span><span class="o">.</span><span class="n">uuid</span><span class="si">}</span><span class="s2">) to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">add_list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">photo_list</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Photo</span><span class="p">]):</span>
<span class="k">for</span> <span class="n">photolist</span> <span class="ow">in</span> <span class="n">chunked</span><span class="p">(</span><span class="n">photo_list</span><span class="p">,</span> <span class="mi">10</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">photolist</span><span class="p">)</span>
<span class="n">photo_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">photo_list</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">verbose</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"Added </span><span class="si">{</span><span class="n">photo_len</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">pluralize</span><span class="p">(</span><span class="n">photo_len</span><span class="p">,</span> <span class="s1">'photo'</span><span class="p">,</span> <span class="s1">'photos'</span><span class="p">)</span><span class="si">}</span><span class="s2"> to album </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">photos</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">photos</span><span class="p">()</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2021, Rhet Turnbull
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</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>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.50.7 documentation</title>
<title>osxphotos.photosdb._photosdb_process_comments - osxphotos 0.50.13 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.50.7 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html" /><link rel="search" title="Search" href="../../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.photosdb.photosdb - osxphotos 0.50.7 documentation</title>
<title>osxphotos.photosdb.photosdb - osxphotos 0.50.13 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.50.7 documentation</div></a>
<a href="../../../index.html"><div class="brand">osxphotos 0.50.13 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -146,7 +146,7 @@
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">osxphotos 0.50.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -3482,6 +3482,8 @@
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">is_reference</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">isreference</span><span class="p">]</span>
<span class="k">elif</span> <span class="n">options</span><span class="o">.</span><span class="n">not_reference</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">isreference</span><span class="p">]</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">in_album</span><span class="p">:</span>
<span class="n">photos</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span> <span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">albums</span><span class="p">]</span>

View File

@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos.queryoptions - osxphotos 0.48.7 documentation</title>
<title>osxphotos.queryoptions - osxphotos 0.50.13 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.7 documentation</div></a>
<a href="../../index.html"><div class="brand">osxphotos 0.50.13 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.7 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -266,6 +266,7 @@
<span class="sd"> not_missing: search for non-missing photos</span>
<span class="sd"> not_panorama: search for non-panorama photos</span>
<span class="sd"> not_portrait: search for non-portrait photos</span>
<span class="sd"> not_reference: search for photos not stored by reference (that is, they are managed by Photos)</span>
<span class="sd"> not_screenshot: search for non-screenshot photos</span>
<span class="sd"> not_selfie: search for non-selfie photos</span>
<span class="sd"> not_shared: search for non-shared photos</span>
@@ -347,6 +348,7 @@
<span class="n">not_missing</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_panorama</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_portrait</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_reference</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_screenshot</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_selfie</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">not_shared</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>

View File

@@ -346,7 +346,7 @@ Template Substitutions
* - {crlf}
- a carriage return + line feed: '\r\n'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.50.9'
- The osxphotos version, e.g. '0.50.13'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

View File

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

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.50.9 documentation</title>
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -817,6 +817,11 @@ to modify this behavior.</p>
<dd><p>Search for photos that were imported as referenced files (not copied into Photos library).</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-not-reference">
<span class="sig-name descname"><span class="pre">--not-reference</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-not-reference" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not references, that is, they were copied into the Photos library and are managed by Photos.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-in-album">
<span class="sig-name descname"><span class="pre">--in-album</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-export-in-album" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are in one or more albums.</p>
@@ -1159,6 +1164,11 @@ to modify this behavior.</p>
<dd><p>Cleanup export directory by deleting any files which were not included in this export set. For example, photos which had previously been exported and were subsequently deleted in Photos. WARNING: cleanup will delete <em>any</em> files in the export directory that were not exported by osxphotos, for example, your own scripts or other files. Be sure this is what you intend before using cleanup. Use dry-run with cleanup first if youre not certain.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-keep">
<span class="sig-name descname"><span class="pre">--keep</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;KEEP_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-keep" title="Permalink to this definition">#</a></dt>
<dd><p>When used with cleanup, prevents file or directory KEEP_PATH from being deleted when cleanup is run. Use this if there are files in the export directory that you dont want to be deleted when cleanup is run. KEEP_PATH may be a file path, e.g. /Volumes/Photos/keep.jpg, or a file path and wild card, e.g. /Volumes/Photos/<em>.txt, or a directory, e.g. /Volumes/Photos/KeepMe. KEEP_PATH may be an absolute path or a relative path. If it is relative, it must be relative to the export destination. For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, you can specify `keep “/Volumes/Photos/</em>.txt”` or <cite>keep “*.txt”</cite>. If wild card is used, KEEP_PATH must be enclosed in quotes to prevent the shell from expanding the wildcard, e.g. <cite>keep “/Volumes/Photos/*.txt”</cite>. If KEEP_PATH is a directory, all files and directories contained in KEEP_PATH will be kept. keep may be repeated to keep additional files/directories.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-export-add-exported-to-album">
<span class="sig-name descname"><span class="pre">--add-exported-to-album</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;ALBUM&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-export-add-exported-to-album" title="Permalink to this definition">#</a></dt>
<dd><p>Add all exported photos to album ALBUM in Photos. Album ALBUM will be created if it doesnt exist. All exported photos will be added to this album. This only works if the Photos library being exported is the last-opened (default) library in Photos. This feature is currently experimental. I dont know how well it will work on large export sets.</p>
@@ -1503,6 +1513,44 @@ Works best with a modern terminal like iTerm2 or Kitty.</p>
<dd><p>Print output in JSON format.</p>
</dd></dl>
</section>
<section id="osxphotos-orphans">
<h3>orphans<a class="headerlink" href="#osxphotos-orphans" title="Permalink to this headline">#</a></h3>
<p>Find orphaned photos in a Photos library</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span>osxphotos orphans <span class="o">[</span>OPTIONS<span class="o">]</span>
</pre></div>
</div>
<p class="rubric">Options</p>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-orphans-export">
<span class="sig-name descname"><span class="pre">--export</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;EXPORT_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-export" title="Permalink to this definition">#</a></dt>
<dd><p>Export orphans to directory EXPORT_PATH. If export not specified, orphans are listed but not exported.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-orphans-db">
<span class="sig-name descname"><span class="pre">--db</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;PHOTOS_LIBRARY_PATH&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-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>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-orphans-V">
<span id="cmdoption-osxphotos-orphans-v"></span><span id="cmdoption-osxphotos-orphans-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-orphans-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-orphans-timestamp">
<span class="sig-name descname"><span class="pre">--timestamp</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-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-orphans-theme">
<span class="sig-name descname"><span class="pre">--theme</span></span><span class="sig-prename descclassname"> <span class="pre">&lt;THEME&gt;</span></span><a class="headerlink" href="#cmdoption-osxphotos-orphans-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>
</section>
<section id="osxphotos-persons">
<h3>persons<a class="headerlink" href="#osxphotos-persons" title="Permalink to this headline">#</a></h3>
<p>Print out persons (faces) found in the Photos library.</p>
@@ -1876,6 +1924,11 @@ if more than one option is provided, they are treated as “AND”
<dd><p>Search for photos that were imported as referenced files (not copied into Photos library).</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-not-reference">
<span class="sig-name descname"><span class="pre">--not-reference</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-not-reference" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not references, that is, they were copied into the Photos library and are managed by Photos.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-query-in-album">
<span class="sig-name descname"><span class="pre">--in-album</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-query-in-album" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are in one or more albums.</p>
@@ -2300,6 +2353,11 @@ if more than one option is provided, they are treated as “AND”
<dd><p>Search for photos that were imported as referenced files (not copied into Photos library).</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-not-reference">
<span class="sig-name descname"><span class="pre">--not-reference</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-not-reference" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are not references, that is, they were copied into the Photos library and are managed by Photos.</p>
</dd></dl>
<dl class="std option">
<dt class="sig sig-object std" id="cmdoption-osxphotos-repl-in-album">
<span class="sig-name descname"><span class="pre">--in-album</span></span><span class="sig-prename descclassname"></span><a class="headerlink" href="#cmdoption-osxphotos-repl-in-album" title="Permalink to this definition">#</a></dt>
<dd><p>Search for photos that are in one or more albums.</p>
@@ -2772,6 +2830,7 @@ Commands:
<li><a class="reference internal" href="#osxphotos-keywords">keywords</a></li>
<li><a class="reference internal" href="#osxphotos-labels">labels</a></li>
<li><a class="reference internal" href="#osxphotos-list">list</a></li>
<li><a class="reference internal" href="#osxphotos-orphans">orphans</a></li>
<li><a class="reference internal" href="#osxphotos-persons">persons</a></li>
<li><a class="reference internal" href="#osxphotos-places">places</a></li>
<li><a class="reference internal" href="#osxphotos-query">query</a></li>

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.50.9 documentation</title>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Index - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -408,6 +408,8 @@
<li><a href="cli.html#cmdoption-osxphotos-keywords-db">osxphotos-keywords command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-labels-db">osxphotos-labels command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-persons-db">osxphotos-persons command line option</a>
</li>
@@ -630,6 +632,13 @@
<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>
</li>
</ul></li>
<li>
--export
<ul>
<li><a href="cli.html#cmdoption-osxphotos-orphans-export">osxphotos-orphans command line option</a>
</li>
</ul></li>
<li>
@@ -939,6 +948,13 @@
<li><a href="cli.html#cmdoption-osxphotos-places-json">osxphotos-places command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-json">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
--keep
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-keep">osxphotos-export command line option</a>
</li>
</ul></li>
<li>
@@ -1173,8 +1189,6 @@
<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
@@ -1186,6 +1200,8 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-not-burst">osxphotos-repl command line option</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
--not-cloudasset
@@ -1288,6 +1304,17 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-portrait">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-portrait">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
--not-reference
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-not-reference">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-reference">osxphotos-query command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">osxphotos-repl command line option</a>
</li>
</ul></li>
<li>
@@ -1753,6 +1780,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-theme">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-inspect-theme">osxphotos-inspect command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-theme">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-theme">osxphotos-timewarp command line option</a>
</li>
@@ -1789,6 +1818,8 @@
<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-orphans-timestamp">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-timestamp">osxphotos-timewarp command line option</a>
</li>
@@ -1956,6 +1987,8 @@
<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-orphans-V">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
</li>
@@ -2150,6 +2183,8 @@
<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-orphans-V">osxphotos-orphans command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-timewarp-V">osxphotos-timewarp command line option</a>
</li>
@@ -2841,10 +2876,10 @@
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_favorite">not_favorite (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hdr">not_hdr (osxphotos.QueryOptions attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#osxphotos.QueryOptions.not_hidden">not_hidden (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_in_album">not_in_album (osxphotos.QueryOptions attribute)</a>
@@ -2858,6 +2893,8 @@
<li><a href="reference.html#osxphotos.QueryOptions.not_panorama">not_panorama (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_portrait">not_portrait (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_reference">not_reference (osxphotos.QueryOptions attribute)</a>
</li>
<li><a href="reference.html#osxphotos.QueryOptions.not_screenshot">not_screenshot (osxphotos.QueryOptions attribute)</a>
</li>
@@ -3122,6 +3159,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-jpeg-ext">--jpeg-ext</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-jpeg-quality">--jpeg-quality</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keep">--keep</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-keyword">--keyword</a>
</li>
@@ -3176,6 +3215,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-not-screenshot">--not-screenshot</a>
</li>
@@ -3412,8 +3453,6 @@
<li><a href="cli.html#cmdoption-osxphotos-keywords-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-labels command line option
@@ -3425,11 +3464,30 @@
<li><a href="cli.html#cmdoption-osxphotos-labels-arg-PHOTOS_LIBRARY">PHOTOS_LIBRARY</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
osxphotos-list command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-list-json">--json</a>
</li>
</ul></li>
<li>
osxphotos-orphans command line option
<ul>
<li><a href="cli.html#cmdoption-osxphotos-orphans-db">--db</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-export">--export</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-theme">--theme</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-timestamp">--timestamp</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">--verbose</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-orphans-V">-V</a>
</li>
</ul></li>
<li>
@@ -3567,6 +3625,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-not-screenshot">--not-screenshot</a>
</li>
@@ -3738,6 +3798,8 @@
<li><a href="cli.html#cmdoption-osxphotos-repl-not-panorama">--not-panorama</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-portrait">--not-portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-reference">--not-reference</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-repl-not-screenshot">--not-screenshot</a>
</li>
@@ -4066,6 +4128,10 @@
<li><a href="cli.html#cmdoption-osxphotos-query-arg-PHOTOS_LIBRARY">osxphotos-query command line option</a>
</li>
</ul></li>
<li><a href="reference.html#osxphotos.PhotosAlbum">PhotosAlbum (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosAlbumPhotoScript">PhotosAlbumPhotoScript (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotosDB">PhotosDB (class in osxphotos)</a>
</li>
<li><a href="reference.html#osxphotos.PhotoTemplate">PhotoTemplate (class in osxphotos)</a>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>osxphotos 0.50.9 documentation</title>
<title>osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="#"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -248,6 +248,7 @@
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-keywords">keywords</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-labels">labels</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-list">list</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-orphans">orphans</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-persons">persons</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-places">places</a></li>
<li class="toctree-l3"><a class="reference internal" href="cli.html#osxphotos-query">query</a></li>

Binary file not shown.

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotoss documentation!" href="index.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos - osxphotos 0.50.9 documentation</title>
<title>OSXPhotos - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos python 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.50.9 documentation</title>
<title>OSXPhotos Python Package Overview - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.50.9 documentation</title>
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Python Module Index - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/><title>Search - osxphotos 0.50.9 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.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Template System - osxphotos 0.50.9 documentation</title>
<title>OSXPhotos Template System - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 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">
@@ -589,7 +589,7 @@
<td><p>a carriage return + line feed: rn</p></td>
</tr>
<tr class="row-even"><td><p>{osxphotos_version}</p></td>
<td><p>The osxphotos version, e.g. 0.50.9</p></td>
<td><p>The osxphotos version, e.g. 0.50.13</p></td>
</tr>
<tr class="row-odd"><td><p>{osxphotos_cmd_line}</p></td>
<td><p>The full command line used to run osxphotos</p></td>

View File

@@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
<meta name="generator" content="sphinx-4.4.0, furo 2022.04.07"/>
<title>OSXPhotos Tutorial - osxphotos 0.50.9 documentation</title>
<title>OSXPhotos Tutorial - osxphotos 0.50.13 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.50.9 documentation</div></a>
<a href="index.html"><div class="brand">osxphotos 0.50.13 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.50.9 documentation</span>
<span class="sidebar-brand-text">osxphotos 0.50.13 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">

View File

@@ -346,7 +346,7 @@ Template Substitutions
* - {crlf}
- a carriage return + line feed: '\r\n'
* - {osxphotos_version}
- The osxphotos version, e.g. '0.50.9'
- The osxphotos version, e.g. '0.50.13'
* - {osxphotos_cmd_line}
- The full command line used to run osxphotos
* - {album}

43
examples/bad_photos.py Normal file
View File

@@ -0,0 +1,43 @@
""" Find 'bad photos' and add them to an album
This is inspired by this blog post: https://www.muchen.ca/blog/2022/cleanup-photos/
This is an osxphotos query function, that when run as follows,
will add all photos with low quality scores to the album 'Bad Photos'
osxphotos query --query-function bad_photos.py::bad_photos --add-to-album "Bad Photos"
"""
from typing import List
from osxphotos import PhotoInfo
# Call this with: osxphotos query --query-function bad_photos.py::bad_photos --add-to-album "Bad Photos"
def bad_photos(photos: List[PhotoInfo]) -> List[PhotoInfo]:
"""your query function should take a list of PhotoInfo objects and return a list of PhotoInfo objects (or empty list)"""
# this example finds bad photos (as measured by Photos' own scoring system)
# don't include screenshots as Photos tends to give low scores to screenshots
return [p for p in photos if not p.screenshot and is_bad_photo(p)]
def is_bad_photo(p: PhotoInfo) -> bool:
"""Look at photo's ScoreInfo to find photos that have low scores
(and hence might be considered bad photos)
"""
return any(
[
p.score.failure < -0.1,
p.score.harmonious_color < -0.1,
p.score.interesting_subject < -0.7,
p.score.intrusive_object_presence < -0.999,
p.score.noise < -0.75,
p.score.pleasant_composition < -0.8,
p.score.pleasant_lighting < -0.7,
p.score.pleasant_perspective < -0.6,
p.score.tastefully_blurred < -0.9,
p.score.well_framed_subject < -0.7,
p.score.well_timed_shot < -0.7,
]
)

196
examples/strip_live.py Normal file
View File

@@ -0,0 +1,196 @@
"""Export selected Live photos and re-import just the image portion"""
import pathlib
import sys
import time
from tempfile import TemporaryDirectory
from typing import List
import click
from photoscript import Album, Photo, PhotosLibrary
from rich import print
from osxphotos import PhotoInfo, PhotosDB
DEFAULT_DELETE_ALBUM = "Live Photos to Delete"
DEFAULT_NEW_ALBUM = "Imported Live Photos"
def rename_photos(photo_paths: List[str]) -> List[str]:
"""Given a list of photo paths, rename the photos so names don't clash as duplicated on re-import"""
# use perf_counter_ns as a simple unique ID to ensure each photo has a different name
new_paths = []
for path in photo_paths:
path = pathlib.Path(path)
stem = f"{path.stem}_{time.perf_counter_ns()}"
new_path = path.rename(path.parent / f"{stem}{path.suffix}")
new_paths.append(str(new_path))
return new_paths
def set_metadata_from_photo(source_photo: PhotoInfo, dest_photos: List[Photo]):
"""Set metadata (keywords, albums, title, description, favorite) for dest_photos from source_photo"""
title = source_photo.title
description = source_photo.description
keywords = source_photo.keywords
favorite = source_photo.favorite
# apply metadata to each photo
for dest_photo in dest_photos:
dest_photo.title = title
dest_photo.description = description
dest_photo.keywords = keywords
dest_photo.favorite = favorite
# add photos to albums
album_ids = [a.uuid for a in source_photo.album_info]
for album_id in album_ids:
album = Album(album_id)
album.add(dest_photos)
def process_photo(
photo: Photo,
photosdb: PhotosDB,
keep_originals: bool,
download_missing: bool,
new_album: Album,
delete_album: Album,
):
"""Process each Live Photo to export/re-import it"""
with TemporaryDirectory() as tempdir:
p = photosdb.get_photo(photo.uuid)
if not p.live_photo:
print(
f"[yellow]Skipping non-Live photo {p.original_filename} ({p.uuid})[/]"
)
return
# versions to download (True for edited, False for original)
versions = []
# use photos_export to download from iCloud
photos_export = False
# try to download missing photos only if photo is missing and --download-missing
if keep_originals or not p.hasadjustments:
# export original photo
if not p.path and not download_missing:
print(
f"[yellow]Skipping missing original version of photo {p.original_filename} ({p.uuid}) (you may want to try --download-missing)[/]"
)
return
photos_export = download_missing and not p.path
versions.append(False)
if p.hasadjustments:
if not p.path_edited and not download_missing:
print(
f"[yellow]Skipping missing edited version of photo {p.original_filename} ({p.uuid}) (you may want to try --download-missing)[/]"
)
return
photos_export = photos_export or (download_missing and not p.path_edited)
versions.append(True)
exported = []
for version in versions:
# export the actual photo (without the Live video)
print(
f"Exporting {'edited' if version else 'original'} photo {p.original_filename} ({p.uuid})"
)
if exports := p.export(
tempdir,
live_photo=False,
edited=version,
use_photos_export=photos_export,
):
exported.extend(exports)
else:
print(
f"[red]Error exporting photo {p.original_filename} ({p.uuid})[/]",
file=sys.stderr,
)
if not exported:
return
exported = rename_photos(exported)
print(
f"Re-importing {', '.join([pathlib.Path(p).name for p in exported])} to album '{new_album.name}'"
)
new_photos = new_album.import_photos(exported)
print("Applying metadata to newly imported photos")
set_metadata_from_photo(p, new_photos)
print(f"Moving {p.original_filename} to album '{delete_album.name}'")
delete_album.add([Photo(p.uuid)])
@click.command()
@click.option(
"--download-missing", is_flag=True, help="Download missing files from iCloud."
)
@click.option(
"--keep-originals",
is_flag=True,
help="If photo is edited, also keep the original, unedited photo. "
"Without --keep-originals, only the edited version of a Live photo that has been edited will be kept.",
)
@click.option(
"--delete-album",
"delete_album_name",
default=DEFAULT_DELETE_ALBUM,
help="Album to put Live photos in when they're ready to be deleted; "
f"default = '{DEFAULT_DELETE_ALBUM}'",
)
@click.option(
"--new-album",
"new_album_name",
default=DEFAULT_NEW_ALBUM,
help="Album to put Live photos in when they've been re-imported after stripping the video component; "
f"default = '{DEFAULT_NEW_ALBUM}'",
)
def strip_live_photos(
download_missing, keep_originals, delete_album_name, new_album_name
):
"""Export selected Live photos and re-import just the image portion.
This script can be used to free space in your Photos library by allowing you
to effectively delete just the Live video portion of a Live photo.
The photo part of the Live photo will be exported to a temporary directory then
reimported into Photos. Albums, keywords, title/caption, favorite, and description
will be preserved. Unfortunately person/face data cannot be preserved.
After export Live photos will be moved to an album (which can be set using
--delete-album) so they can be deleted. You can use Command + Delete to put the
photos in the trash after selecting them in the album.
"""
photoslib = PhotosLibrary()
selected = photoslib.selection
if not selected:
print("No photos selected...nothing to do", file=sys.stderr)
sys.exit(1)
print(f"Processing {len(selected)} photo(s)")
print("Loading Photos database")
photosdb = PhotosDB()
new_album = photoslib.album(
new_album_name, top_level=True
) or photoslib.create_album(new_album_name)
delete_album = photoslib.album(
delete_album_name, top_level=True
) or photoslib.create_album(delete_album_name)
for photo in selected:
process_photo(
photo, photosdb, keep_originals, download_missing, new_album, delete_album
)
new_album.spotlight()
if __name__ == "__main__":
strip_live_photos()

View File

@@ -12,6 +12,7 @@ from .momentinfo import MomentInfo
from .personinfo import PersonInfo
from .photoexporter import ExportOptions, ExportResults, PhotoExporter
from .photoinfo import PhotoInfo
from .photosalbum import PhotosAlbum, PhotosAlbumPhotoScript
from .photosdb import PhotosDB
from .photosdb._photosdb_process_comments import CommentInfo, LikeInfo
from .phototemplate import PhotoTemplate
@@ -43,6 +44,8 @@ __all__ = [
"PhotoExporter",
"PhotoInfo",
"PhotoTemplate",
"PhotosAlbum",
"PhotosAlbumPhotoScript",
"PhotosDB",
"PlaceInfo",
"ProjectInfo",

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.50.9"
__version__ = "0.50.13"

View File

@@ -58,6 +58,7 @@ from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
from .labels import labels
from .list import _list_libraries, list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
@@ -88,6 +89,7 @@ __all__ = [
"list_libraries",
"list_libraries",
"load_uuid_from_file",
"orphans",
"persons",
"photo_inspect",
"places",

View File

@@ -20,6 +20,7 @@ from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
from .labels import labels
from .list import list_libraries
from .orphans import orphans
from .persons import persons
from .photo_inspect import photo_inspect
from .places import places
@@ -79,6 +80,7 @@ for command in [
keywords,
labels,
list_libraries,
orphans,
persons,
photo_inspect,
places,

View File

@@ -420,6 +420,12 @@ def QUERY_OPTIONS(f):
is_flag=True,
help="Search for photos that were imported as referenced files (not copied into Photos library).",
),
o(
"--not-reference",
is_flag=True,
help="Search for photos that are not references, that is, they were copied into the Photos library "
"and are managed by Photos.",
),
o(
"--in-album",
is_flag=True,

View File

@@ -11,7 +11,7 @@ import shlex
import subprocess
import sys
import time
from typing import Dict
from typing import Iterable, List, Tuple
import click
import osxmetadata
@@ -546,6 +546,26 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
"for example, your own scripts or other files. Be sure this is what you intend before using "
"--cleanup. Use --dry-run with --cleanup first if you're not certain.",
)
@click.option(
"--keep",
metavar="KEEP_PATH",
nargs=1,
multiple=True,
help="When used with --cleanup, prevents file or directory KEEP_PATH from being deleted "
"when cleanup is run. Use this if there are files in the export directory that you don't "
"want to be deleted when --cleanup is run. "
"KEEP_PATH may be a file path, e.g. '/Volumes/Photos/keep.jpg', "
"or a file path and wild card, e.g. '/Volumes/Photos/*.txt', "
"or a directory, e.g. '/Volumes/Photos/KeepMe'. "
"KEEP_PATH may be an absolute path or a relative path. "
"If it is relative, it must be relative to the export destination. "
"For example if export destination is `/Volumes/Photos` and you want to keep all `.txt` files, "
'you can specify `--keep "/Volumes/Photos/*.txt"` or `--keep "*.txt"`. '
"If wild card is used, KEEP_PATH must be enclosed in quotes to prevent the shell from expanding the wildcard, "
'e.g. `--keep "/Volumes/Photos/*.txt"`. '
"If KEEP_PATH is a directory, all files and directories contained in KEEP_PATH will be kept. "
"--keep may be repeated to keep additional files/directories.",
)
@click.option(
"--add-exported-to-album",
metavar="ALBUM",
@@ -757,6 +777,7 @@ def export(
is_reference,
jpeg_ext,
jpeg_quality,
keep,
keyword_template,
keyword,
label,
@@ -784,6 +805,7 @@ def export(
not_live,
not_panorama,
not_portrait,
not_reference,
not_screenshot,
not_selfie,
not_shared,
@@ -974,8 +996,10 @@ def export(
ignore_date_modified = cfg.ignore_date_modified
ignore_signature = cfg.ignore_signature
in_album = cfg.in_album
is_reference = cfg.is_reference
jpeg_ext = cfg.jpeg_ext
jpeg_quality = cfg.jpeg_quality
keep = cfg.keep
keyword = cfg.keyword
keyword_template = cfg.keyword_template
label = cfg.label
@@ -1002,6 +1026,7 @@ def export(
not_live = cfg.not_live
not_panorama = cfg.not_panorama
not_portrait = cfg.not_portrait
not_reference = cfg.not_reference
not_screenshot = cfg.not_screenshot
not_selfie = cfg.not_selfie
not_shared = cfg.not_shared
@@ -1107,6 +1132,7 @@ def export(
("slow_mo", "not_slow_mo"),
("time_lapse", "not_time_lapse"),
("title", "no_title"),
("is_reference", "not_reference"),
]
dependent_options = [
("exiftool_merge_keywords", ("exiftool", "sidecar")),
@@ -1115,6 +1141,7 @@ def export(
("exiftool_option", ("exiftool")),
("ignore_signature", ("update", "force_update")),
("jpeg_quality", ("convert_to_jpeg")),
("keep", ("cleanup")),
("missing", ("download_missing", "use_photos_export")),
("only_new", ("update", "force_update")),
("append", ("report")),
@@ -1368,6 +1395,7 @@ def export(
not_missing=None,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,
@@ -1689,9 +1717,18 @@ def export(
+ [r[0] for r in results.error]
+ db_files
)
# if --report, add report file to keep list to prevent it from being deleted
if report:
all_files.append(report)
dirs_to_keep = []
if keep:
files_to_keep, dirs_to_keep = collect_files_to_keep(keep, dest)
all_files += files_to_keep
rich_echo(f"Cleaning up [filepath]{dest}")
cleaned_files, cleaned_dirs = cleanup_files(
dest, all_files, fileutil, verbose_=verbose_
dest, all_files, dirs_to_keep, fileutil, verbose_=verbose_
)
file_str = "files" if len(cleaned_files) != 1 else "file"
dir_str = "directories" if len(cleaned_dirs) != 1 else "directory"
@@ -2453,7 +2490,6 @@ def find_files_in_branch(pathname, filename):
# walk down the tree
for root, _, filenames in os.walk(pathname):
# for directory in directories:
# print(os.path.join(root, directory))
for fname in filenames:
if fname == filename and pathlib.Path(root) != pathname:
files.append(os.path.join(root, fname))
@@ -2470,14 +2506,43 @@ def find_files_in_branch(pathname, filename):
return files
def cleanup_files(dest_path, files_to_keep, fileutil, verbose_):
def collect_files_to_keep(
keep: Iterable[str], export_dir: str
) -> Tuple[List[str], List[str]]:
"""Collect all files to keep for --keep/--cleanup.
Args:
keep: Iterable of filepaths to keep; each path may be a filepath, a filepath/wildcard, or a directory path.
export_dir: the export directory which will be used to resolve paths when paths in keep are relative instead of absolute
Returns:
tuple of [files_to_keep], [dirs_to_keep]
"""
export_dir = pathlib.Path(export_dir)
keepers = []
for k in keep:
keeper = pathlib.Path(k).expanduser()
if not keeper.is_absolute():
# relative path: relative to export_dir
keeper = export_dir / keeper
if keeper.is_dir():
keepers.extend(keeper.glob("**/*"))
keepers.extend(keeper.parent.glob(keeper.name))
files_to_keep = [str(k) for k in keepers if k.is_file()]
dirs_to_keep = [str(k) for k in keepers if k.is_dir()]
return files_to_keep, dirs_to_keep
def cleanup_files(dest_path, files_to_keep, dirs_to_keep, fileutil, verbose_):
"""cleanup dest_path by deleting and files and empty directories
not in files_to_keep
Args:
dest_path: path to directory to clean
files_to_keep: list of full file paths to keep (not delete)
fileutile: FileUtil object
dirs_to_keep: list of full dir paths to keep (not delete if they are empty)
fileutil: FileUtil object
verbose_: verbose callable for printing verbose output
Returns:
tuple of (list of files deleted, list of directories deleted)
@@ -2497,6 +2562,8 @@ def cleanup_files(dest_path, files_to_keep, fileutil, verbose_):
deleted_dirs = []
# walk directory tree bottom up and verify contents are empty
for dirpath, _, _ in os.walk(dest_path, topdown=False):
if dirpath in dirs_to_keep:
continue
if not list(pathlib.Path(dirpath).glob("*")):
# directory and directory is empty
verbose_(f"Deleting empty directory {dirpath}")

156
osxphotos/cli/orphans.py Normal file
View File

@@ -0,0 +1,156 @@
"""Find orphaned photos in a Photos library"""
import os
import os.path
import re
import sys
# using os.path.join is slightly slower inside loop than directly using the method
from os.path import join as joinpath
from os.path import splitext
from pathlib import Path
from typing import Dict
import click
from osxphotos import PhotosDB
from osxphotos._constants import _PHOTOS_4_VERSION
from osxphotos.fileutil import FileUtil
from osxphotos.utils import increment_filename, pluralize
from .click_rich_echo import rich_click_echo as echo
from .click_rich_echo import 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 .help import get_help_msg
from .list import _list_libraries
from .verbose import get_verbose_console, verbose_print
@click.command(name="orphans")
@click.option(
"--export",
metavar="EXPORT_PATH",
required=False,
type=click.Path(file_okay=False, writable=True, resolve_path=True, exists=True),
help="Export orphans to directory EXPORT_PATH. If --export not specified, orphans are listed but not exported.",
)
@DB_OPTION
@click.option("--verbose", "-V", "verbose", is_flag=True, help="Print verbose output.")
@click.option("--timestamp", is_flag=True, help="Add time stamp to verbose output")
@THEME_OPTION
@click.pass_obj
@click.pass_context
def orphans(ctx, cli_obj, export, db, verbose, timestamp, theme):
"""Find orphaned photos in a Photos library"""
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)
# below needed for to make CliRunner work for testing
cli_db = cli_obj.db if cli_obj is not None else None
db = get_photos_db(db, cli_db)
if not db:
echo(get_help_msg(orphans), err=True)
echo("\n\nLocated the following Photos library databases: ", err=True)
_list_libraries()
return
verbose_("Loading Photos database")
photosdb = PhotosDB(dbfile=db, verbose=verbose_, rich=True)
if photosdb.db_version <= _PHOTOS_4_VERSION:
echo(
"[error]Orphans can only be used with Photos libraries > version 5 (MacOS Catalina/10.15)[/]",
err=True,
)
sys.exit(1)
photos = photosdb.photos()
photos += photosdb.photos(intrash=True)
# need to add unselected bursts
burst_photos = [bp for p in photos for bp in p.burst_photos]
# will be some duplicates but those will be removed when converting to dict
photos.extend(burst_photos)
uuids_in_db = {photo.uuid: photo for photo in photos}
# walk the Photos library looking for photos associated with a uuid
uuids_in_library = {}
verbose_("Scanning for orphan files")
# originals
verbose_("Scanning original files")
directory = joinpath(photosdb.library_path, "originals")
scan_for_files(directory, uuids_in_library)
# edited
verbose_("Scanning edited files")
directory = joinpath(photosdb.library_path, "resources", "renders")
scan_for_files(directory, uuids_in_library)
# derivatives
verbose_("Scanning derivative files")
directory = joinpath(photosdb.library_path, "resources", "derivatives")
scan_for_files(directory, uuids_in_library)
# shared iCloud photos
verbose_("Scanning shared iCloud photos")
directory = joinpath(photosdb.library_path, "resources", "cloudsharing", "data")
scan_for_files(directory, uuids_in_library)
# shared derivatives
directory = joinpath(
"resources", "cloudsharing", "resources", "derivatives", "masters"
)
scan_for_files(directory, uuids_in_library)
# find orphans
possible_orphans = []
for uuid, files in uuids_in_library.items():
if uuid not in uuids_in_db:
possible_orphans.extend(files)
echo(
f"Found [num]{len(possible_orphans)}[/] "
f"{pluralize(len(possible_orphans), 'orphan', 'orphans')}"
)
exported = []
for orphan in possible_orphans:
echo(f"[filepath]{orphan}[/]")
if export:
dest = increment_filename(Path(export) / Path(orphan).name)
verbose_(f"Copying [filepath]{Path(orphan).name}[/] to [filepath]{dest}[/]")
FileUtil.copy(orphan, dest)
exported.append(dest)
if export:
echo(
f"Exported [num]{len(exported)}[/] "
f"{pluralize(len(exported), 'file', 'files')}"
)
def scan_for_files(directory: str, uuid_dict: Dict):
"""Walk a directory path finding any files named with UUID in the filename and add to uuid_dict
Note: modifies uuid_dict
"""
uuid_pattern = r"([0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})"
uuid_regex = re.compile(uuid_pattern)
for dirpath, dirname, filenames in os.walk(directory):
for filename in filenames:
if match := uuid_regex.match(filename):
stem, ext = splitext(filename)
# .plist and .aae files may hold data on adjustments but these
# are not useful by themselves so skip them
if ext.lower() in [".plist", ".aae"]:
continue
filepath = joinpath(dirpath, filename)
try:
uuid_dict[match[0]].append(filepath)
except KeyError:
uuid_dict[match[0]] = [filepath]

View File

@@ -127,6 +127,7 @@ def query(
not_missing,
not_panorama,
not_portrait,
not_reference,
not_screenshot,
not_selfie,
not_shared,
@@ -176,7 +177,6 @@ def query(
from_date,
from_time,
has_raw,
is_reference,
keyword,
label,
max_size,
@@ -220,6 +220,7 @@ def query(
(shared, not_shared),
(slow_mo, not_slow_mo),
(time_lapse, not_time_lapse),
(is_reference, not_reference),
]
# print help if no non-exclusive term or a double exclusive term is given
if any(all(bb) for bb in exclusive) or not any(
@@ -309,6 +310,7 @@ def query(
not_missing=not_missing,
not_panorama=not_panorama,
not_portrait=not_portrait,
not_reference=not_reference,
not_screenshot=not_screenshot,
not_selfie=not_selfie,
not_shared=not_shared,

View File

@@ -253,7 +253,6 @@ def _query_options_from_kwargs(**kwargs) -> QueryOptions:
"from_date",
"from_time",
"has_raw",
"is_reference",
"keyword",
"label",
"max_size",
@@ -294,6 +293,7 @@ def _query_options_from_kwargs(**kwargs) -> QueryOptions:
("shared", "not_shared"),
("slow_mo", "not_slow_mo"),
("time_lapse", "not_time_lapse"),
("is_reference", "not_reference"),
]
# print help if no non-exclusive term or a double exclusive term is given
# TODO: add option to validate requiring at least one query arg

Binary file not shown.

View File

@@ -3286,6 +3286,8 @@ class PhotosDB:
if options.is_reference:
photos = [p for p in photos if p.isreference]
elif options.not_reference:
photos = [p for p in photos if not p.isreference]
if options.in_album:
photos = [p for p in photos if p.albums]

View File

@@ -70,6 +70,7 @@ class QueryOptions:
not_missing: search for non-missing photos
not_panorama: search for non-panorama photos
not_portrait: search for non-portrait photos
not_reference: search for photos not stored by reference (that is, they are managed by Photos)
not_screenshot: search for non-screenshot photos
not_selfie: search for non-selfie photos
not_shared: search for non-shared photos
@@ -151,6 +152,7 @@ class QueryOptions:
not_missing: Optional[bool] = None
not_panorama: Optional[bool] = None
not_portrait: Optional[bool] = None
not_reference: Optional[bool] = None
not_screenshot: Optional[bool] = None
not_selfie: Optional[bool] = None
not_shared: Optional[bool] = None

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

View File

@@ -5899,6 +5899,7 @@ def test_export_cleanup():
"--dry-run",
],
)
assert result.exit_code == 0
assert "Deleted: 2 files, 0 directories" in result.output
assert pathlib.Path("./delete_me.txt").is_file()
assert pathlib.Path("./foo/delete_me_too.txt").is_file()
@@ -5913,6 +5914,35 @@ def test_export_cleanup():
assert not pathlib.Path("./foo/delete_me_too.txt").is_file()
def test_export_cleanup_report():
"""test export with --cleanup flag with --report in the export dir (#739)"""
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"])
assert result.exit_code == 0
tmpdir = os.getcwd()
# run cleanup without dry-run
result = runner.invoke(
export,
[
os.path.join(cwd, CLI_PHOTOS_DB),
".",
"-V",
"--update",
"--cleanup",
"--report",
f"{tmpdir}/report.db",
],
)
assert "Deleted: 0 files, 0 directories" in result.output
assert pathlib.Path("./report.db").is_file()
def test_export_cleanup_empty_album():
"""test export with --cleanup flag with an empty album (#481)"""
@@ -6055,6 +6085,165 @@ def test_export_cleanup_exiftool_accented_album_name_same_filenames():
assert "Deleted: 0 files, 0 directories" in result.output
def test_export_cleanup_keep():
"""test export with --cleanup --keep options"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
tmpdir = os.getcwd()
result = runner.invoke(export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V"])
assert result.exit_code == 0
# create file and a directory that should be deleted
os.mkdir("./empty_dir")
os.mkdir("./delete_me_dir")
with open("./delete_me.txt", "w") as fd:
fd.write("delete me!")
with open("./delete_me_dir/delete_me.txt", "w") as fd:
fd.write("delete me!")
# create files and directories that should be kept
os.mkdir("./keep_me")
os.mkdir("./keep_me/keep_me_2")
with open("./keep_me.txt", "w") as fd:
fd.write("keep me!")
with open("./report.db", "w") as fd:
fd.write("keep me!")
with open("./keep_me/keep_me.txt", "w") as fd:
fd.write("keep me")
# run cleanup with dry-run
result = runner.invoke(
export,
[
os.path.join(cwd, CLI_PHOTOS_DB),
".",
"-V",
"--update",
"--cleanup",
"--keep",
f"{tmpdir}/keep_me",
"--keep",
f"{tmpdir}/keep_me.txt",
"--keep",
f"{tmpdir}/*.db",
"--dry-run",
],
)
assert "Deleted: 2 files, 1 directory" in result.output
assert pathlib.Path("./delete_me.txt").is_file()
assert pathlib.Path("./delete_me_dir/delete_me.txt").is_file()
assert pathlib.Path("./empty_dir").is_dir()
# run cleanup without dry-run
result = runner.invoke(
export,
[
os.path.join(cwd, CLI_PHOTOS_DB),
".",
"-V",
"--update",
"--cleanup",
"--keep",
f"{tmpdir}/keep_me",
"--keep",
f"{tmpdir}/keep_me.txt",
"--keep",
f"{tmpdir}/*.db",
],
)
assert "Deleted: 2 files, 2 directories" in result.output
assert not pathlib.Path("./delete_me.txt").is_file()
assert not pathlib.Path("./delete_me_dir/delete_me_too.txt").is_file()
assert not pathlib.Path("./empty_dir").is_dir()
assert pathlib.Path("./keep_me.txt").is_file()
assert pathlib.Path("./keep_me").is_dir()
assert pathlib.Path("./keep_me/keep_me.txt").is_file()
assert pathlib.Path("./keep_me/keep_me_2").is_dir()
assert pathlib.Path("./report.db").is_file()
def test_export_cleanup_keep_relative_path():
"""test export with --cleanup --keep options with relative paths"""
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"])
assert result.exit_code == 0
# create file and a directory that should be deleted
os.mkdir("./empty_dir")
os.mkdir("./delete_me_dir")
with open("./delete_me.txt", "w") as fd:
fd.write("delete me!")
with open("./delete_me_dir/delete_me.txt", "w") as fd:
fd.write("delete me!")
# create files and directories that should be kept
os.mkdir("./keep_me")
os.mkdir("./keep_me/keep_me_2")
with open("./keep_me.txt", "w") as fd:
fd.write("keep me!")
with open("./report.db", "w") as fd:
fd.write("keep me!")
with open("./keep_me/keep_me.txt", "w") as fd:
fd.write("keep me")
# run cleanup with dry-run
result = runner.invoke(
export,
[
os.path.join(cwd, CLI_PHOTOS_DB),
".",
"-V",
"--update",
"--cleanup",
"--keep",
"keep_me",
"--keep",
"keep_me.txt",
"--keep",
"*.db",
"--dry-run",
],
)
assert "Deleted: 2 files, 1 directory" in result.output
assert pathlib.Path("./delete_me.txt").is_file()
assert pathlib.Path("./delete_me_dir/delete_me.txt").is_file()
assert pathlib.Path("./empty_dir").is_dir()
# run cleanup without dry-run
result = runner.invoke(
export,
[
os.path.join(cwd, CLI_PHOTOS_DB),
".",
"-V",
"--update",
"--cleanup",
"--keep",
"keep_me",
"--keep",
"keep_me.txt",
"--keep",
"*.db",
],
)
assert "Deleted: 2 files, 2 directories" in result.output
assert not pathlib.Path("./delete_me.txt").is_file()
assert not pathlib.Path("./delete_me_dir/delete_me_too.txt").is_file()
assert not pathlib.Path("./empty_dir").is_dir()
assert pathlib.Path("./keep_me.txt").is_file()
assert pathlib.Path("./keep_me").is_dir()
assert pathlib.Path("./keep_me/keep_me.txt").is_file()
assert pathlib.Path("./keep_me/keep_me_2").is_dir()
assert pathlib.Path("./report.db").is_file()
def test_save_load_config():
"""test --save-config, --load-config"""

35
tests/test_cli_orphans.py Normal file
View File

@@ -0,0 +1,35 @@
"""Test `osxphotos orphan` CLI"""
import os.path
from click.testing import CliRunner
from osxphotos.cli.orphans import orphans
from .test_cli import PHOTOS_DB_15_7
def test_orphans():
"""test basic orphans"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
orphans, ["--db", os.path.join(cwd, PHOTOS_DB_15_7), "-V"]
)
assert result.exit_code == 0
assert "Found 1 orphan" in result.output
def test_orphans_export():
"""test export of orphans"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
orphans, ["--db", os.path.join(cwd, PHOTOS_DB_15_7), "--export", ".", "-V"]
)
assert result.exit_code == 0
assert "Exported 1 file" in result.output