Added support for filtering only movies or photos to CLI; added search for UTI to CLI
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.19.00"
|
||||
__version__ = "0.19.01"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,23 +3,23 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BackgroundHighlightCollection</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T09:24:37Z</date>
|
||||
<key>BackgroundHighlightEnrichment</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T09:24:37Z</date>
|
||||
<key>BackgroundJobAssetRevGeocode</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T10:47:44Z</date>
|
||||
<key>BackgroundJobSearch</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T09:24:37Z</date>
|
||||
<key>BackgroundPeopleSuggestion</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T09:24:36Z</date>
|
||||
<key>BackgroundUserBehaviorProcessor</key>
|
||||
<date>2019-12-28T23:29:58Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey</key>
|
||||
<date>2019-12-29T06:18:45Z</date>
|
||||
<date>2019-12-29T10:47:45Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key>
|
||||
<date>2019-12-28T23:29:57Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key>
|
||||
<date>2019-12-29T06:18:40Z</date>
|
||||
<date>2019-12-29T09:24:37Z</date>
|
||||
<key>SiriPortraitDonation</key>
|
||||
<date>2019-12-28T23:29:58Z</date>
|
||||
</dict>
|
||||
|
||||
Reference in New Issue
Block a user