From d51d7a41e48844c640b6b5c104227bcdcc6262f5 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sat, 3 Apr 2021 20:23:03 -0700 Subject: [PATCH] Added --name to search filename, closes #249, #412 --- README.md | 5 ++++ osxphotos/_version.py | 2 +- osxphotos/cli.py | 58 ++++++++++++++++++++++++++++++++---- tests/test_cli.py | 69 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 53ce550d..59ab55ce 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,11 @@ Options: searches top level folders (e.g. does not look at subfolders) + --name FILENAME Search for photos with filename matching + FILENAME. If more than one --name options is + specified, they are treated as "OR", e.g. find + photos matching any FILENAME. + --uuid UUID Search for photos with UUID(s). --uuid-from-file FILE Search for photos with UUID(s) loaded from FILE. Format is a single UUID per line. Lines diff --git a/osxphotos/_version.py b/osxphotos/_version.py index b23f5939..b3b28601 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.41.7" +__version__ = "0.41.8" diff --git a/osxphotos/cli.py b/osxphotos/cli.py index 3fe3a3e7..0ed80f26 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -238,6 +238,15 @@ def query_options(f): 'If more than one folder, treated as "OR", e.g. find photos in any FOLDER. ' "Only searches top level folders (e.g. does not look at subfolders)", ), + o( + "--name", + metavar="FILENAME", + default=None, + multiple=True, + help="Search for photos with filename matching FILENAME. " + 'If more than one --name options is specified, they are treated as "OR", ' + "e.g. find photos matching any FILENAME. ", + ), o( "--uuid", metavar="UUID", @@ -871,6 +880,7 @@ def export( album, folder, uuid, + name, uuid_from_file, title, no_title, @@ -1020,6 +1030,7 @@ def export( person = cfg.person album = cfg.album folder = cfg.folder + name = cfg.name uuid = cfg.uuid uuid_from_file = cfg.uuid_from_file title = cfg.title @@ -1421,6 +1432,7 @@ def export( burst_photos=export_bursts, # skip missing bursts if using --download-missing by itself as AppleScript otherwise causes errors missing_bursts=(download_missing and use_photokit) or not download_missing, + name=name, ) if photos: @@ -1645,6 +1657,7 @@ def query( person, album, folder, + name, uuid, uuid_from_file, title, @@ -1718,6 +1731,7 @@ def query( person, album, folder, + name, uuid, uuid_from_file, edited, @@ -1851,6 +1865,7 @@ def query( is_reference=is_reference, in_album=in_album, not_in_album=not_in_album, + name=name, ) # below needed for to make CliRunner work for testing @@ -2028,6 +2043,7 @@ def _query( not_in_album=False, burst_photos=None, missing_bursts=None, + name=None, ): """Run a query against PhotosDB to extract the photos based on user supply criteria used by query and export commands @@ -2094,30 +2110,38 @@ def _query( if title: # search title field for text # if more than one, find photos with all title values in title + photo_list = [] if ignore_case: # case-insensitive for t in title: t = t.lower() - photos = [p for p in photos if p.title and t in p.title.lower()] + photo_list.extend( + [p for p in photos if p.title and t in p.title.lower()] + ) else: for t in title: - photos = [p for p in photos if p.title and t in p.title] + photo_list.extend([p for p in photos if p.title and t in p.title]) + photos = photo_list elif no_title: photos = [p for p in photos if not p.title] if description: # search description field for text # if more than one, find photos with all description values in description + photo_list = [] if ignore_case: # case-insensitive for d in description: d = d.lower() - photos = [ - p for p in photos if p.description and d in p.description.lower() - ] + photo_list.extend( + [p for p in photos if p.description and d in p.description.lower()] + ) else: for d in description: - photos = [p for p in photos if p.description and d in p.description] + photo_list.extend( + [p for p in photos if p.description and d in p.description] + ) + photos = photo_list elif no_description: photos = [p for p in photos if not p.description] @@ -2292,6 +2316,28 @@ def _query( seen_uuids[p.uuid] = p photos = list(seen_uuids.values()) + if name: + # search filename fields for text + # if more than one, find photos with all title values in filename + photo_list = [] + if ignore_case: + # case-insensitive + for n in name: + n = n.lower() + photo_list.extend( + [ + p + for p in photos + if n in p.filename.lower() or n in p.original_filename.lower() + ] + ) + else: + for n in name: + photo_list.extend( + [p for p in photos if n in p.filename or n in p.original_filename] + ) + photos = photo_list + return photos diff --git a/tests/test_cli.py b/tests/test_cli.py index 6951543c..f1f1893e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -5699,3 +5699,72 @@ def test_export_burst_folder_album(): path = folder_album / filename assert path.is_file() + +def test_query_name(): + """ test query --name """ + import json + import os + import os.path + import osxphotos + from osxphotos.cli import query + + runner = CliRunner() + cwd = os.getcwd() + result = runner.invoke( + query, + ["--json", "--db", os.path.join(cwd, PHOTOS_DB_15_7), "--name", "DSC03584"], + ) + assert result.exit_code == 0 + json_got = json.loads(result.output) + + assert len(json_got) == 1 + assert json_got[0]["original_filename"] == "DSC03584.dng" + + +def test_query_name_i(): + """ test query --name -i """ + import json + import os + import os.path + import osxphotos + from osxphotos.cli import query + + runner = CliRunner() + cwd = os.getcwd() + result = runner.invoke( + query, + [ + "--json", + "--db", + os.path.join(cwd, PHOTOS_DB_15_7), + "--name", + "dsc03584", + "-i", + ], + ) + assert result.exit_code == 0 + json_got = json.loads(result.output) + + assert len(json_got) == 1 + assert json_got[0]["original_filename"] == "DSC03584.dng" + + +def test_export_name(): + """ test export --name """ + import glob + import os + import os.path + import osxphotos + from osxphotos.cli import export + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + result = runner.invoke( + export, [os.path.join(cwd, PHOTOS_DB_15_7), ".", "-V", "--name", "DSC03584"] + ) + assert result.exit_code == 0 + files = glob.glob("*") + assert len(files) == 1 +