diff --git a/osxphotos/__main__.py b/osxphotos/__main__.py index 2cc76e52..27ad9750 100644 --- a/osxphotos/__main__.py +++ b/osxphotos/__main__.py @@ -18,6 +18,7 @@ from .utils import create_path_by_date # TODO: add "--any" to search any field (e.g. keyword, description, title contains "wedding") (add case insensitive option) # TODO: add search for filename + class CLI_Obj: def __init__(self, db=None, json=False, debug=False): if debug: @@ -78,7 +79,6 @@ def albums(cli_obj): click.echo(yaml.dump(albums, sort_keys=False)) - @cli.command() @click.pass_obj def persons(cli_obj): @@ -112,7 +112,6 @@ def info(cli_obj): shared_movies = [p for p in movies if p.shared] info["shared_movie_count"] = len(shared_movies) - keywords = pdb.keywords_as_dict info["keywords_count"] = len(keywords) @@ -214,6 +213,12 @@ def list_libraries(cli_obj): @click.option( "--no-description", is_flag=True, help="Search for photos with no description." ) +@click.option( + "--uti", + default=None, + multiple=False, + help="Search for photos whose uniform type identifier (UTI) matches TEXT", +) @click.option( "-i", "--ignore-case", @@ -237,10 +242,24 @@ def list_libraries(cli_obj): help="Search for photos present on disk (e.g. not missing).", ) @click.option( - "--shared", is_flag=True, help="Search for photos in shared iCloud album (Photos 5 only)." + "--shared", + is_flag=True, + help="Search for photos in shared iCloud album (Photos 5 only).", ) @click.option( - "--not-shared", is_flag=True, help="Search for photos not in shared iCloud album (Photos 5 only)." + "--not-shared", + is_flag=True, + help="Search for photos not in shared iCloud album (Photos 5 only).", +) +@click.option( + "--only-movies", + is_flag=True, + help="Search only for movies (default searches both images and movies)", +) +@click.option( + "--only-photos", + is_flag=True, + help="Search only for photos/images (default searches both images and movies)", ) @click.option( "--json", @@ -274,6 +293,9 @@ def query( not_missing, shared, not_shared, + only_movies, + only_photos, + uti, ): """ Query the Photos database using 1 or more search options; if more than one option is provided, they are treated as "AND" @@ -301,6 +323,9 @@ def query( not_missing, shared, not_shared, + only_movies, + only_photos, + uti, ] ): click.echo(cli.commands["query"].get_help(ctx)) @@ -325,31 +350,45 @@ def query( # can't search for both description and no_description click.echo(cli.commands["query"].get_help(ctx)) return - else: - photos = _query( - cli_obj, - keyword, - person, - album, - uuid, - title, - no_title, - description, - no_description, - ignore_case, - json, - edited, - external_edit, - favorite, - not_favorite, - hidden, - not_hidden, - missing, - not_missing, - shared, - not_shared, - ) - print_photo_info(photos, cli_obj.json or json) + elif only_photos and only_movies: + # can't have only photos and only movies + click.echo(cli.commands["query"].get_help(ctx)) + return + + # actually have something to query + isphoto = ismovie = True # default searches for everything + if only_movies: + isphoto = False + if only_photos: + ismovie = False + + photos = _query( + cli_obj, + keyword, + person, + album, + uuid, + title, + no_title, + description, + no_description, + ignore_case, + json, + edited, + external_edit, + favorite, + not_favorite, + hidden, + not_hidden, + missing, + not_missing, + shared, + not_shared, + isphoto, + ismovie, + uti, + ) + print_photo_info(photos, cli_obj.json or json) @cli.command() @@ -370,6 +409,12 @@ def query( @click.option( "--no-description", is_flag=True, help="Search for photos with no description." ) +@click.option( + "--uti", + default=None, + multiple=False, + help="Search for photos whose uniform type identifier (UTI) matches TEXT", +) @click.option( "-i", "--ignore-case", @@ -387,10 +432,14 @@ def query( @click.option("--hidden", is_flag=True, help="Search for photos marked hidden.") @click.option("--not-hidden", is_flag=True, help="Search for photos not marked hidden.") @click.option( - "--shared", is_flag=True, help="Search for photos in shared iCloud album (Photos 5 only)." + "--shared", + is_flag=True, + help="Search for photos in shared iCloud album (Photos 5 only).", ) @click.option( - "--not-shared", is_flag=True, help="Search for photos not in shared iCloud album (Photos 5 only)." + "--not-shared", + is_flag=True, + help="Search for photos not in shared iCloud album (Photos 5 only).", ) @click.option("--verbose", is_flag=True, help="Print verbose output.") @click.option( @@ -427,6 +476,16 @@ def query( '"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).", ) +@click.option( + "--only-movies", + is_flag=True, + help="Search only for movies (default searches both images and movies)", +) +@click.option( + "--only-photos", + is_flag=True, + help="Search only for photos/images (default searches both images and movies)", +) @click.argument("dest", nargs=1) @click.pass_obj @click.pass_context @@ -441,6 +500,7 @@ def export( no_title, description, no_description, + uti, ignore_case, edited, external_edit, @@ -456,6 +516,8 @@ def export( export_edited, original_name, sidecar, + only_photos, + only_movies, dest, ): """ Export photos from the Photos database. @@ -471,7 +533,34 @@ def export( if not os.path.isdir(dest): sys.exit("DEST must be valid path") - # if no query terms, show help and return + # sanity check input args + if favorite and not_favorite: + # can't search for both favorite and notfavorite + click.echo(cli.commands["export"].get_help(ctx)) + return + elif hidden and not_hidden: + # can't search for both hidden and nothidden + click.echo(cli.commands["export"].get_help(ctx)) + return + elif title and no_title: + # can't search for both title and no_title + click.echo(cli.commands["export"].get_help(ctx)) + return + elif description and no_description: + # can't search for both description and no_description + click.echo(cli.commands["export"].get_help(ctx)) + return + elif only_photos and only_movies: + # can't have only photos and only movies + click.echo(cli.commands["export"].get_help(ctx)) + return + + isphoto = ismovie = True # default searches for everything + if only_movies: + isphoto = False + if only_photos: + ismovie = False + photos = _query( cli_obj, keyword, @@ -494,6 +583,9 @@ def export( None, # not-missing shared, not_shared, + isphoto, + ismovie, + uti, ) if photos: @@ -579,6 +671,9 @@ def print_photo_info(photos, json=False): "latitude", "longitude", "path_edited", + "isphoto", + "ismovie", + "uti", ] ) for p in photos: @@ -587,7 +682,7 @@ def print_photo_info(photos, json=False): p.uuid, p.filename, p.original_filename, - str(p.date), + p.date.isoformat(), p.description, p.title, ", ".join(p.keywords), @@ -603,6 +698,9 @@ def print_photo_info(photos, json=False): p._latitude, p._longitude, p.path_edited, + p.isphoto, + p.ismovie, + p.uti, ] ) for row in dump: @@ -631,6 +729,9 @@ def _query( not_missing, shared, not_shared, + isphoto, + ismovie, + uti, ): """ run a query against PhotosDB to extract the photos based on user supply criteria """ """ used by query and export commands """ @@ -638,7 +739,14 @@ def _query( """ if either is modified, need to ensure all three functions are updated """ photosdb = osxphotos.PhotosDB(dbfile=cli_obj.db) - photos = photosdb.photos(keywords=keyword, persons=person, albums=album, uuid=uuid, movies=True) + photos = photosdb.photos( + keywords=keyword, + persons=person, + albums=album, + uuid=uuid, + images=isphoto, + movies=ismovie, + ) if title: # search title field for text @@ -656,7 +764,7 @@ def _query( if description: # search description field for text - # if more than one, find photos with all name values in in description + # if more than one, find photos with all name values in description if ignore_case: # case-insensitive for d in description: @@ -696,6 +804,14 @@ def _query( elif not_shared: photos = [p for p in photos if not p.shared] + if shared: + photos = [p for p in photos if p.shared] + elif not_shared: + photos = [p for p in photos if not p.shared] + + if uti: + photos = [p for p in photos if uti in p.uti] + return photos diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 35187c0e..c5c03cd4 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.19.00" +__version__ = "0.19.01" diff --git a/osxphotos/photoinfo.py b/osxphotos/photoinfo.py index b6b96a01..b2f83d8b 100644 --- a/osxphotos/photoinfo.py +++ b/osxphotos/photoinfo.py @@ -536,6 +536,9 @@ class PhotoInfo: "longitude": self._longitude, "path_edited": self.path_edited, "shared": self.shared, + "isphoto": self.isphoto, + "ismovie": self.ismovie, + "uti": self.uti, } return yaml.dump(info, sort_keys=False) @@ -561,6 +564,9 @@ class PhotoInfo: "longitude": self._longitude, "path_edited": self.path_edited, "shared": self.shared, + "isphoto": self.isphoto, + "ismovie": self.ismovie, + "uti": self.uti, } return json.dumps(pic) diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite index 9de2ca33..3f1f8920 100644 Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite differ diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal index 383bdba3..e69de29b 100644 Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite index 33d086f0..68334f5c 100644 Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite differ diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm index c029a43f..33bbfc22 100644 Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db index 0a4131ab..64b773a1 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm index 078c7da7..6531da6c 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm index 87ad1dd1..2ea5db73 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal index 142592ee..945b46a5 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm index a32e80c1..0f38a9a2 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal index 21f0cef7..d8537442 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm index 2919f3ce..d7d50c85 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal index 4fb26534..1d5b069a 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm index f3212eb1..50705cbc 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal index 700f3c01..e172c860 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm index 44c14f17..f94f5fae 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal index 3a29cf02..8dbf6ba5 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal differ diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist index 3a80739c..6224d5fa 100644 --- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist +++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist @@ -3,23 +3,23 @@ BackgroundHighlightCollection - 2019-12-29T06:18:40Z + 2019-12-29T09:24:37Z BackgroundHighlightEnrichment - 2019-12-29T06:18:40Z + 2019-12-29T09:24:37Z BackgroundJobAssetRevGeocode - 2019-12-29T06:18:40Z + 2019-12-29T10:47:44Z BackgroundJobSearch - 2019-12-29T06:18:40Z + 2019-12-29T09:24:37Z BackgroundPeopleSuggestion - 2019-12-29T06:18:40Z + 2019-12-29T09:24:36Z BackgroundUserBehaviorProcessor 2019-12-28T23:29:58Z PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey - 2019-12-29T06:18:45Z + 2019-12-29T10:47:45Z PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate 2019-12-28T23:29:57Z PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate - 2019-12-29T06:18:40Z + 2019-12-29T09:24:37Z SiriPortraitDonation 2019-12-28T23:29:58Z diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb index 22f25203..907a8f0e 100644 Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb differ