12
CHANGELOG.md
12
CHANGELOG.md
@@ -4,6 +4,18 @@ 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).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [v0.22.4](https://github.com/RhetTbull/osxphotos/compare/v0.22.0...v0.22.4)
|
||||||
|
|
||||||
|
> 20 January 2020
|
||||||
|
|
||||||
|
- Add --from-date and --to-date to query and export command [`#57`](https://github.com/RhetTbull/osxphotos/pull/57)
|
||||||
|
- Refactor CLI [`#55`](https://github.com/RhetTbull/osxphotos/pull/55)
|
||||||
|
- Refactor cli: singular --db, --json and query options. [`e214746`](https://github.com/RhetTbull/osxphotos/commit/e214746063271e6f9f586286103ed051ada49d85)
|
||||||
|
- Implement from_date and to_date in PhotosDB as well as query and export command. Some refactoring of CLI as well. [`cfa2b4a`](https://github.com/RhetTbull/osxphotos/commit/cfa2b4a828facf0aff5bc19f777457ad776c4a05)
|
||||||
|
- Refactored _query. Still hairy, but less so. [`b9dee49`](https://github.com/RhetTbull/osxphotos/commit/b9dee4995c6d89fadb3d2482374b7098f2ab5ed9)
|
||||||
|
- Updated README.md [`0aff83f`](https://github.com/RhetTbull/osxphotos/commit/0aff83ff21c20e293c0b75bacf2863090a0fb725)
|
||||||
|
- Started adding tests for CLI [`f0b18c3`](https://github.com/RhetTbull/osxphotos/commit/f0b18c3d29b2141d348be0495013c51c072c6251)
|
||||||
|
|
||||||
#### [v0.22.0](https://github.com/RhetTbull/osxphotos/compare/v0.21.5...v0.22.0)
|
#### [v0.22.0](https://github.com/RhetTbull/osxphotos/compare/v0.21.5...v0.22.0)
|
||||||
|
|
||||||
> 18 January 2020
|
> 18 January 2020
|
||||||
|
|||||||
226
README.md
226
README.md
@@ -11,63 +11,14 @@
|
|||||||
* [Example uses of the module](#example-uses-of-the-module)
|
* [Example uses of the module](#example-uses-of-the-module)
|
||||||
* [Module Interface](#module-interface)
|
* [Module Interface](#module-interface)
|
||||||
+ [PhotosDB](#photosdb)
|
+ [PhotosDB](#photosdb)
|
||||||
- [Read a Photos library database](#read-a-photos-library-database)
|
|
||||||
- [Open System Photos library](#open-system-photos-library)
|
|
||||||
- [Open a specific Photos library](#open-a-specific-photos-library)
|
|
||||||
- [`keywords`](#keywords)
|
|
||||||
- [`albums`](#albums)
|
|
||||||
- [`albums_shared`](#albums_shared)
|
|
||||||
- [`persons`](#persons)
|
|
||||||
- [`keywords_as_dict`](#keywords_as_dict)
|
|
||||||
- [`persons_as_dict`](#persons_as_dict)
|
|
||||||
- [`albums_as_dict`](#albums_as_dict)
|
|
||||||
- [`albums_shared_as_dict`](#albums_shared_as_dict)
|
|
||||||
- [`library_path`](#library_path)
|
|
||||||
- [`db_path`](#db_path)
|
|
||||||
- [`db_version`](#db_version)
|
|
||||||
- [` photos(keywords=None, uuid=None, persons=None, albums=None, images=True, movies=False)`](#-photoskeywordsnone-uuidnone-personsnone-albumsnone-imagestrue-moviesfalse)
|
|
||||||
+ [PhotoInfo](#photoinfo)
|
+ [PhotoInfo](#photoinfo)
|
||||||
- [`uuid`](#uuid)
|
|
||||||
- [`filename`](#filename)
|
|
||||||
- [`original_filename`](#original_filename)
|
|
||||||
- [`date`](#date)
|
|
||||||
- [`description`](#description)
|
|
||||||
- [`title`](#title)
|
|
||||||
- [`keywords`](#keywords-1)
|
|
||||||
- [`albums`](#albums-1)
|
|
||||||
- [`persons`](#persons-1)
|
|
||||||
- [`path`](#path)
|
|
||||||
- [`path_edited`](#path_edited)
|
|
||||||
- [`ismissing`](#ismissing)
|
|
||||||
- [`hasadjustments`](#hasadjustments)
|
|
||||||
- [`external_edit`](#external_edit)
|
|
||||||
- [`favorite`](#favorite)
|
|
||||||
- [`hidden`](#hidden)
|
|
||||||
- [`location`](#location)
|
|
||||||
- [`shared`](#shared)
|
|
||||||
- [`isphoto`](#isphoto)
|
|
||||||
- [`ismovie`](#ismovie)
|
|
||||||
- [`iscloudasset`](#iscloudasset)
|
|
||||||
- [`incloud`](#incloud)
|
|
||||||
- [`uti`](#uti)
|
|
||||||
- [`burst`](#burst)
|
|
||||||
- [`burst_photos`](#burst_photos)
|
|
||||||
- [`live_photo`](#live_photo)
|
|
||||||
- [`path_live_photo`](#path_live_photo)
|
|
||||||
- [`json()`](#json)
|
|
||||||
- [`export(dest, *filename, edited=False, overwrite=False, increment=True, sidecar=False, use_photos_export=False, timeout=120)`](#exportdest-filename-editedfalse-overwritefalse-incrementtrue-sidecarfalse-use_photos_exportfalse-timeout120)
|
|
||||||
+ [Utility Functions](#utility-functions)
|
+ [Utility Functions](#utility-functions)
|
||||||
- [```get_system_library_path()```](#get_system_library_path)
|
|
||||||
- [```get_last_library_path()```](#get_last_library_path)
|
|
||||||
- [```list_photo_libraries()```](#list_photo_libraries)
|
|
||||||
- [```dd_to_dms_str(lat, lon)```](#dd_to_dms_strlat-lon)
|
|
||||||
- [```create_path_by_date(dest, dt)```](#create_path_by_datedest-dt)
|
|
||||||
+ [Examples](#examples)
|
+ [Examples](#examples)
|
||||||
* [Related Projects](#related-projects)
|
* [Related Projects](#related-projects)
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
* [Implementation Notes](#implementation-notes)
|
* [Implementation Notes](#implementation-notes)
|
||||||
* [Dependencies](#dependencies)
|
* [Dependencies](#dependencies)
|
||||||
* [Acknowledgements](#acknowledgements)
|
* [Acknowledgements](#acknowledgements)
|
||||||
|
|
||||||
## What is osxphotos?
|
## What is osxphotos?
|
||||||
|
|
||||||
@@ -133,79 +84,98 @@ Usage: osxphotos export [OPTIONS] [PHOTOS_LIBRARY]... DEST
|
|||||||
photos will be exported.
|
photos will be exported.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--db <Photos database path> Specify Photos database path.
|
--db <Photos database path> Specify Photos database path. Path to Photos
|
||||||
--keyword TEXT Search for keyword(s).
|
library/database can be specified using
|
||||||
--person TEXT Search for person(s).
|
either --db or directly as PHOTOS_LIBRARY
|
||||||
--album TEXT Search for album(s).
|
positional argument. If neither --db or
|
||||||
--uuid TEXT Search for UUID(s).
|
PHOTOS_LIBRARY provided, will attempt to
|
||||||
--title TEXT Search for TEXT in title of photo.
|
find the library to use in the following
|
||||||
--no-title Search for photos with no title.
|
order: 1. last opened library, 2. system
|
||||||
--description TEXT Search for TEXT in description of photo.
|
library, 3. ~/Pictures/Photos
|
||||||
--no-description Search for photos with no description.
|
Library.photoslibrary
|
||||||
--uti TEXT Search for photos whose uniform type identifier
|
--keyword TEXT Search for keyword(s).
|
||||||
(UTI) matches TEXT
|
--person TEXT Search for person(s).
|
||||||
-i, --ignore-case Case insensitive search for title or
|
--album TEXT Search for album(s).
|
||||||
description. Does not apply to keyword, person,
|
--uuid TEXT Search for UUID(s).
|
||||||
or album.
|
--title TEXT Search for TEXT in title of photo.
|
||||||
--edited Search for photos that have been edited.
|
--no-title Search for photos with no title.
|
||||||
--external-edit Search for photos edited in external editor.
|
--description TEXT Search for TEXT in description of photo.
|
||||||
--favorite Search for photos marked favorite.
|
--no-description Search for photos with no description.
|
||||||
--not-favorite Search for photos not marked favorite.
|
--uti TEXT Search for photos whose uniform type
|
||||||
--hidden Search for photos marked hidden.
|
identifier (UTI) matches TEXT
|
||||||
--not-hidden Search for photos not marked hidden.
|
-i, --ignore-case Case insensitive search for title or
|
||||||
--burst Search for photos that were taken in a burst.
|
description. Does not apply to keyword,
|
||||||
--not-burst Search for photos that are not part of a burst.
|
person, or album.
|
||||||
--live Search for Apple live photos
|
--edited Search for photos that have been edited.
|
||||||
--not-live Search for photos that are not Apple live
|
--external-edit Search for photos edited in external editor.
|
||||||
photos
|
--favorite Search for photos marked favorite.
|
||||||
--shared Search for photos in shared iCloud album
|
--not-favorite Search for photos not marked favorite.
|
||||||
(Photos 5 only).
|
--hidden Search for photos marked hidden.
|
||||||
--not-shared Search for photos not in shared iCloud album
|
--not-hidden Search for photos not marked hidden.
|
||||||
(Photos 5 only).
|
--shared Search for photos in shared iCloud album
|
||||||
-V, --verbose Print verbose output.
|
(Photos 5 only).
|
||||||
--overwrite Overwrite existing files. Default behavior is
|
--not-shared Search for photos not in shared iCloud album
|
||||||
to add (1), (2), etc to filename if file
|
(Photos 5 only).
|
||||||
already exists. Use this with caution as it may
|
--burst Search for photos that were taken in a
|
||||||
create name collisions on export. (e.g. if two
|
burst.
|
||||||
files happen to have the same name)
|
--not-burst Search for photos that are not part of a
|
||||||
--export-by-date Automatically create output folders to organize
|
burst.
|
||||||
photos by date created (e.g.
|
--live Search for Apple live photos
|
||||||
DEST/2019/12/20/photoname.jpg).
|
--not-live Search for photos that are not Apple live
|
||||||
--export-edited Also export edited version of photo if an
|
photos
|
||||||
edited version exists. Edited photo will be
|
--only-movies Search only for movies (default searches
|
||||||
named in form of "photoname_edited.ext"
|
both images and movies).
|
||||||
--export-bursts If a photo is a burst photo export all
|
--only-photos Search only for photos/images (default
|
||||||
associated burst images in the library.
|
searches both images and movies).
|
||||||
--export-live If a photo is a live photo export the
|
--from-date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]
|
||||||
associated live video component. Live video
|
Search by start item date, e.g.
|
||||||
will have same name as photo but with .mov
|
2000-01-12T12:00:00 or 2000-12-31 (ISO 8601
|
||||||
extension.
|
w/o TZ).
|
||||||
--original-name Use photo's original filename instead of
|
--to-date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]
|
||||||
current filename for export.
|
Search by end item date, e.g.
|
||||||
--sidecar Create JSON sidecar for each photo exported in
|
2000-01-12T12:00:00 or 2000-12-31 (ISO 8601
|
||||||
format useable by exiftool
|
w/o TZ).
|
||||||
(https://exiftool.org/) The sidecar file can be
|
-V, --verbose Print verbose output.
|
||||||
used to apply metadata to the file with
|
--overwrite Overwrite existing files. Default behavior
|
||||||
exiftool, for example: "exiftool
|
is to add (1), (2), etc to filename if file
|
||||||
-j=photoname.jpg.json photoname.jpg" The
|
already exists. Use this with caution as it
|
||||||
sidecar file is named in format
|
may create name collisions on export. (e.g.
|
||||||
photoname.ext.json where ext is extension of
|
if two files happen to have the same name)
|
||||||
the photo (e.g. jpg). Note: this does not
|
--export-by-date Automatically create output folders to
|
||||||
create an XMP sidecar as used by Lightroom,
|
organize photos by date created (e.g.
|
||||||
etc.
|
DEST/2019/12/20/photoname.jpg).
|
||||||
--only-movies Search only for movies (default searches both
|
--export-edited Also export edited version of photo if an
|
||||||
images and movies).
|
edited version exists. Edited photo will be
|
||||||
--only-photos Search only for photos/images (default searches
|
named in form of "photoname_edited.ext"
|
||||||
both images and movies).
|
--export-bursts If a photo is a burst photo export all
|
||||||
--download-missing Attempt to download missing photos from iCloud.
|
associated burst images in the library.
|
||||||
The current implementation uses Applescript to
|
--export-live If a photo is a live photo export the
|
||||||
interact with Photos to export the photo which
|
associated live video component. Live video
|
||||||
will force Photos to download from iCloud if
|
will have same name as photo but with .mov
|
||||||
the photo does not exist on disk. This will be
|
extension.
|
||||||
slow and will require internet connection. This
|
--original-name Use photo's original filename instead of
|
||||||
obviously only works if the Photos library is
|
current filename for export.
|
||||||
synched to iCloud.
|
--sidecar Create JSON sidecar for each photo exported
|
||||||
-h, --help Show this message and exit.
|
in format useable by exiftool
|
||||||
|
(https://exiftool.org/) The sidecar file can
|
||||||
|
be used to apply metadata to the file with
|
||||||
|
exiftool, for example: "exiftool
|
||||||
|
-j=photoname.jpg.json photoname.jpg" The
|
||||||
|
sidecar file is named in format
|
||||||
|
photoname.ext.json where ext is extension of
|
||||||
|
the photo (e.g. jpg). Note: this does not
|
||||||
|
create an XMP sidecar as used by Lightroom,
|
||||||
|
etc.
|
||||||
|
--download-missing Attempt to download missing photos from
|
||||||
|
iCloud. The current implementation uses
|
||||||
|
Applescript to interact with Photos to
|
||||||
|
export the photo which will force Photos to
|
||||||
|
download from iCloud if the photo does not
|
||||||
|
exist on disk. This will be slow and will
|
||||||
|
require internet connection. This obviously
|
||||||
|
only works if the Photos library is synched
|
||||||
|
to iCloud.
|
||||||
|
-h, --help Show this message and exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
Example: export all photos to ~/Desktop/export, including edited versions and live photo movies, group in folders by date created
|
Example: export all photos to ~/Desktop/export, including edited versions and live photo movies, group in folders by date created
|
||||||
@@ -449,11 +419,11 @@ photosdb.db_version
|
|||||||
Returns the version number for Photos library database. You likely won't need this but it's provided in case needed for debugging. PhotosDB will print a warning to `sys.stderr` if you open a database version that has not been tested.
|
Returns the version number for Photos library database. You likely won't need this but it's provided in case needed for debugging. PhotosDB will print a warning to `sys.stderr` if you open a database version that has not been tested.
|
||||||
|
|
||||||
|
|
||||||
#### ` photos(keywords=None, uuid=None, persons=None, albums=None, images=True, movies=False)`
|
#### ` photos(keywords=None, uuid=None, persons=None, albums=None, images=True, movies=False, from_date=None, to_date=None)`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# assumes photosdb is a PhotosDB object (see above)
|
# assumes photosdb is a PhotosDB object (see above)
|
||||||
photos = photosdb.photos([keywords=['keyword',]], [uuid=['uuid',]], [persons=['person',]], [albums=['album',]])
|
photos = photosdb.photos([keywords=['keyword',]], [uuid=['uuid',]], [persons=['person',]], [albums=['album',]],[from_date=datetime.datetime],[to_date=datetime.datetime])
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns a list of [PhotoInfo](#PhotoInfo) objects. Each PhotoInfo object represents a photo in the Photos Libary.
|
Returns a list of [PhotoInfo](#PhotoInfo) objects. Each PhotoInfo object represents a photo in the Photos Libary.
|
||||||
@@ -469,6 +439,8 @@ photos = photosdb.photos(
|
|||||||
albums = [],
|
albums = [],
|
||||||
images = bool,
|
images = bool,
|
||||||
movies = bool,
|
movies = bool,
|
||||||
|
from_date = datetime.datetime,
|
||||||
|
to_date = datetime.datetime
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -478,8 +450,10 @@ photos = photosdb.photos(
|
|||||||
- ```albums```: list of one or more album names. Returns only photos contained in the album(s). If more than one album name is provided, returns photos contained in any of the albums (.e.g. treated as "or")
|
- ```albums```: list of one or more album names. Returns only photos contained in the album(s). If more than one album name is provided, returns photos contained in any of the albums (.e.g. treated as "or")
|
||||||
- ```images```: bool; if True, returns photos/images; default is True
|
- ```images```: bool; if True, returns photos/images; default is True
|
||||||
- ```movies```: bool; if True, returns movies/videos; default is False
|
- ```movies```: bool; if True, returns movies/videos; default is False
|
||||||
|
- ```from_date```: datetime.datetime; if provided, finds photos where creation date >= from_date; default is None
|
||||||
|
- ```to_date```: datetime.datetime; if provided, finds photos where creation date <= to_date; default is None
|
||||||
|
|
||||||
If more than one of (keywords, uuid, persons, albums) is provided, they are treated as "and" criteria. E.g.
|
If more than one of (keywords, uuid, persons, albums,from_date, to_date) is provided, they are treated as "and" criteria. E.g.
|
||||||
|
|
||||||
Finds all photos with (keyword = "wedding" or "birthday") and (persons = "Juan Rodriguez")
|
Finds all photos with (keyword = "wedding" or "birthday") and (persons = "Juan Rodriguez")
|
||||||
|
|
||||||
|
|||||||
34
examples/photos_repl.py
Executable file
34
examples/photos_repl.py
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env python3 -i
|
||||||
|
|
||||||
|
# open an interactive REPL with photosdb and photos defined
|
||||||
|
# as osxphotos.PhotosDB() and PhotosDB.photos respectively
|
||||||
|
# useful for debugging or exploring the Photos database
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# click needed since this uses a couple of functions from CLI (__main__.py)
|
||||||
|
import click
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import get_photos_db, _list_libraries
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
db = None
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
db = sys.argv[1]
|
||||||
|
else:
|
||||||
|
db = get_photos_db()
|
||||||
|
|
||||||
|
if db:
|
||||||
|
return osxphotos.PhotosDB(dbfile=db)
|
||||||
|
else:
|
||||||
|
_list_libraries()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(f"Version: {osxphotos._version.__version__}")
|
||||||
|
photosdb = main()
|
||||||
|
photos = photosdb.photos(images=True, movies=True)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.22.1"
|
__version__ = "0.22.5"
|
||||||
|
|||||||
@@ -1318,6 +1318,8 @@ class PhotosDB:
|
|||||||
albums=None,
|
albums=None,
|
||||||
images=True,
|
images=True,
|
||||||
movies=False,
|
movies=False,
|
||||||
|
from_date=None,
|
||||||
|
to_date=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Return a list of PhotoInfo objects
|
Return a list of PhotoInfo objects
|
||||||
@@ -1326,9 +1328,11 @@ class PhotosDB:
|
|||||||
If more than one arg, returns photos matching all the criteria (e.g. keywords AND persons)
|
If more than one arg, returns photos matching all the criteria (e.g. keywords AND persons)
|
||||||
images: if True, returns image files, if False, does not return images; default is True
|
images: if True, returns image files, if False, does not return images; default is True
|
||||||
movies: if True, returns movie files, if False, does not return movies; default is False
|
movies: if True, returns movie files, if False, does not return movies; default is False
|
||||||
|
from_date: return photos with creation date >= from_date (datetime.datetime object, default None)
|
||||||
|
to_date: return photos with creation date <= to_date (datetime.datetime object, default None)
|
||||||
"""
|
"""
|
||||||
photos_sets = [] # list of photo sets to perform intersection of
|
photos_sets = [] # list of photo sets to perform intersection of
|
||||||
if not keywords and not uuid and not persons and not albums:
|
if not any([keywords, uuid, persons, albums, from_date, to_date]):
|
||||||
# return all the photos, filtering for images and movies
|
# return all the photos, filtering for images and movies
|
||||||
# append keys of all photos as a single set to photos_sets
|
# append keys of all photos as a single set to photos_sets
|
||||||
photos_sets.append(set(self._dbphotos.keys()))
|
photos_sets.append(set(self._dbphotos.keys()))
|
||||||
@@ -1372,6 +1376,19 @@ class PhotosDB:
|
|||||||
photos_sets.append(set(self._dbfaces_person[person]))
|
photos_sets.append(set(self._dbfaces_person[person]))
|
||||||
else:
|
else:
|
||||||
logging.debug(f"Could not find person '{person}' in database")
|
logging.debug(f"Could not find person '{person}' in database")
|
||||||
|
if from_date or to_date:
|
||||||
|
dsel = self._dbphotos
|
||||||
|
if from_date:
|
||||||
|
dsel = {
|
||||||
|
k: v for k, v in dsel.items() if v["imageDate"] >= from_date
|
||||||
|
}
|
||||||
|
logging.debug(
|
||||||
|
f"Found %i items with from_date {from_date}" % len(dsel)
|
||||||
|
)
|
||||||
|
if to_date:
|
||||||
|
dsel = {k: v for k, v in dsel.items() if v["imageDate"] <= to_date}
|
||||||
|
logging.debug(f"Found %i items with to_date {to_date}" % len(dsel))
|
||||||
|
photos_sets.append(set(dsel.keys()))
|
||||||
|
|
||||||
photoinfo = []
|
photoinfo = []
|
||||||
if photos_sets: # found some photos
|
if photos_sets: # found some photos
|
||||||
|
|||||||
@@ -289,6 +289,24 @@ def create_path_by_date(dest, dt):
|
|||||||
return new_dest
|
return new_dest
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: this doesn't always work, still looking for a way to
|
||||||
|
# force Photos to open the library being operated on
|
||||||
|
# def _open_photos_library_applescript(library_path):
|
||||||
|
# """ Force Photos to open a specific library
|
||||||
|
# library_path: path to the Photos library """
|
||||||
|
# open_scpt = AppleScript(
|
||||||
|
# f"""
|
||||||
|
# on openLibrary
|
||||||
|
# tell application "Photos"
|
||||||
|
# activate
|
||||||
|
# open POSIX file "{library_path}"
|
||||||
|
# end tell
|
||||||
|
# end openLibrary
|
||||||
|
# """
|
||||||
|
# )
|
||||||
|
# open_scpt.run()
|
||||||
|
|
||||||
|
|
||||||
def _export_photo_uuid_applescript(
|
def _export_photo_uuid_applescript(
|
||||||
uuid, dest, original=True, edited=False, timeout=120
|
uuid, dest, original=True, edited=False, timeout=120
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -786,3 +786,20 @@ def test_photosinfo_repr():
|
|||||||
k: str(v).encode("utf-8")
|
k: str(v).encode("utf-8")
|
||||||
for k, v in photo2.__dict__.items()
|
for k, v in photo2.__dict__.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_to_date():
|
||||||
|
import osxphotos
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(PHOTOS_DB)
|
||||||
|
|
||||||
|
photos = photosdb.photos(from_date=dt.datetime(2018, 10, 28))
|
||||||
|
assert len(photos) == 2
|
||||||
|
|
||||||
|
photos = photosdb.photos(to_date=dt.datetime(2018, 10, 28))
|
||||||
|
assert len(photos) == 5
|
||||||
|
|
||||||
|
photos = photosdb.photos(from_date=dt.datetime(2018, 9, 28),
|
||||||
|
to_date=dt.datetime(2018, 9, 29))
|
||||||
|
assert len(photos) == 4
|
||||||
|
|||||||
127
tests/test_cli.py
Normal file
127
tests/test_cli.py
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import pytest
|
||||||
|
from click.testing import CliRunner
|
||||||
|
|
||||||
|
CLI_OUTPUT_NO_SUBCOMMAND = [
|
||||||
|
"Options:",
|
||||||
|
"--db <Photos database path> Specify Photos database path. Path to Photos",
|
||||||
|
"library/database can be specified using either",
|
||||||
|
"--db or directly as PHOTOS_LIBRARY positional",
|
||||||
|
"argument.",
|
||||||
|
"--json Print output in JSON format.",
|
||||||
|
"-v, --version Show the version and exit.",
|
||||||
|
"-h, --help Show this message and exit.",
|
||||||
|
"Commands:",
|
||||||
|
" albums Print out albums found in the Photos library.",
|
||||||
|
" dump Print list of all photos & associated info from the Photos",
|
||||||
|
" export Export photos from the Photos database.",
|
||||||
|
" help Print help; for help on commands: help <command>.",
|
||||||
|
" info Print out descriptive info of the Photos library database.",
|
||||||
|
" keywords Print out keywords found in the Photos library.",
|
||||||
|
" list Print list of Photos libraries found on the system.",
|
||||||
|
" persons Print out persons (faces) found in the Photos library.",
|
||||||
|
" query Query the Photos database using 1 or more search options; if",
|
||||||
|
]
|
||||||
|
|
||||||
|
CLI_OUTPUT_QUERY_UUID = '[{"uuid": "D79B8D77-BFFC-460B-9312-034F2877D35B", "filename": "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg", "original_filename": "Pumkins2.jpg", "date": "2018-09-28T16:07:07-04:00", "description": "Girl holding pumpkin", "title": "I found one!", "keywords": ["Kids"], "albums": ["Pumpkin Farm", "Test Album"], "persons": ["Katie"], "path": "/tests/Test-10.15.1.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg", "ismissing": false, "hasadjustments": false, "external_edit": false, "favorite": false, "hidden": false, "latitude": null, "longitude": null, "path_edited": null, "shared": false, "isphoto": true, "ismovie": false, "uti": "public.jpeg", "burst": false, "live_photo": false, "path_live_photo": null, "iscloudasset": false, "incloud": null}]'
|
||||||
|
|
||||||
|
CLI_EXPORT_FILENAMES = [
|
||||||
|
"Pumkins1.jpg",
|
||||||
|
"Pumkins2.jpg",
|
||||||
|
"Pumpkins3.jpg",
|
||||||
|
"St James Park.jpg",
|
||||||
|
"St James Park_edited.jpg",
|
||||||
|
"Tulips.jpg",
|
||||||
|
"wedding.jpg",
|
||||||
|
"wedding_edited.jpg",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_osxphotos():
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import cli
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(cli, [])
|
||||||
|
output = result.output
|
||||||
|
assert result.exit_code == 0
|
||||||
|
for line in CLI_OUTPUT_NO_SUBCOMMAND:
|
||||||
|
assert line in output
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_uuid():
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import query
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(
|
||||||
|
query,
|
||||||
|
[
|
||||||
|
"--json",
|
||||||
|
"--db",
|
||||||
|
"./tests/Test-10.15.1.photoslibrary",
|
||||||
|
"--uuid",
|
||||||
|
"D79B8D77-BFFC-460B-9312-034F2877D35B",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_expected = json.loads(CLI_OUTPUT_QUERY_UUID)[0]
|
||||||
|
json_got = json.loads(result.output)[0]
|
||||||
|
|
||||||
|
assert list(json_expected.keys()).sort() == list(json_got.keys()).sort()
|
||||||
|
|
||||||
|
# check values expected vs got
|
||||||
|
# path needs special handling as path is set to full path which will differ system to system
|
||||||
|
for key_ in json_expected:
|
||||||
|
assert key_ in json_got
|
||||||
|
if key_ != "path":
|
||||||
|
assert json_expected[key_] == json_got[key_]
|
||||||
|
else:
|
||||||
|
assert json_expected[key_] in json_got[key_]
|
||||||
|
|
||||||
|
|
||||||
|
def test_export():
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, "tests/Test-10.15.1.photoslibrary"),
|
||||||
|
".",
|
||||||
|
"--original-name",
|
||||||
|
"--export-edited",
|
||||||
|
"-V",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
files = glob.glob("*.jpg")
|
||||||
|
assert files.sort() == CLI_EXPORT_FILENAMES.sort()
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_date():
|
||||||
|
import json
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import query
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(
|
||||||
|
query,
|
||||||
|
[
|
||||||
|
"--json",
|
||||||
|
"--db",
|
||||||
|
"./tests/Test-10.15.1.photoslibrary",
|
||||||
|
"--from-date=2018-09-28",
|
||||||
|
"--to-date=2018-09-28T23:00:00"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
json_got = json.loads(result.output)
|
||||||
|
assert len(json_got) == 4
|
||||||
Reference in New Issue
Block a user