From 55c088eea2ddecb14e362221da9e2a7c0f403780 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sun, 10 Jan 2021 09:44:42 -0800 Subject: [PATCH] Added --jpeg-ext, implements #330 --- README.md | 13 ++++ osxphotos/__main__.py | 60 ++++++++++++----- osxphotos/_version.py | 4 +- osxphotos/photoinfo/_photoinfo_export.py | 5 +- tests/search_info_test_data_10_15_7.json | 2 +- tests/test_cli.py | 82 ++++++++++++++++++++++++ utils/README.md | 18 ++++++ utils/copy_uuid_to_clipboard.applescript | 20 ++++++ 8 files changed, 182 insertions(+), 22 deletions(-) create mode 100644 utils/README.md create mode 100644 utils/copy_uuid_to_clipboard.applescript diff --git a/README.md b/README.md index e1febf49..8a86adb7 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,10 @@ Options: --export-as-hardlink Hardlink files instead of copying them. Cannot be used with --exiftool which creates copies of the files with embedded EXIF data. + Note: on APFS volumes, files are cloned when + exporting giving many of the same advantages + as hardlinks without having to use --export- + as-hardlink. --touch-file Sets the file's modification time to match photo date. --overwrite Overwrite existing files. Default behavior @@ -489,6 +493,15 @@ Options: do not include an extension in the FILENAME template. See below for additional details on templating system. + --jpeg-ext EXTENSION Specify file extension for JPEG files. + Photos uses .jpeg for edited images but many + images are imported with .jpg or .JPG which + can result in multiple different extensions + used for JPEG files upon export. Use --jpg- + ext to specify a single extension to use for + all exported JPEG images. Valid values are + jpeg, jpg, JPEG, JPG; e.g. '--jpg-ext jpg' + to use '.jpg' for all JPEGs. --strip Optionally strip leading and trailing whitespace from any rendered templates. For example, if --filename template is "{title,} diff --git a/osxphotos/__main__.py b/osxphotos/__main__.py index 40e203bf..f89de600 100644 --- a/osxphotos/__main__.py +++ b/osxphotos/__main__.py @@ -47,6 +47,7 @@ from .path_utils import is_valid_filepath, sanitize_filename, sanitize_filepath from .photoinfo import ExportResults from .photokit import check_photokit_authorization, request_photokit_authorization from .phototemplate import TEMPLATE_SUBSTITUTIONS, TEMPLATE_SUBSTITUTIONS_MULTI_VALUED +from .utils import get_preferred_uti_extension # global variable to control verbose output # set via --verbose/-V @@ -1588,6 +1589,16 @@ def query( "File extension will be added automatically--do not include an extension in the FILENAME template. " "See below for additional details on templating system.", ) +@click.option( + "--jpeg-ext", + multiple=False, + metavar="EXTENSION", + type=click.Choice(["jpeg", "jpg", "JPEG", "JPG"], case_sensitive=True), + help="Specify file extension for JPEG files. Photos uses .jpeg for edited images but many images " + "are imported with .jpg or .JPG which can result in multiple different extensions used for JPEG files " + "upon export. Use --jpg-ext to specify a single extension to use for all exported JPEG images. " + "Valid values are jpeg, jpg, JPEG, JPG; e.g. '--jpg-ext jpg' to use '.jpg' for all JPEGs.", +) @click.option( "--strip", is_flag=True, @@ -1759,6 +1770,7 @@ def export( has_raw, directory, filename_template, + jpeg_ext, strip, edited_suffix, original_suffix, @@ -1898,6 +1910,7 @@ def export( has_raw = cfg.has_raw directory = cfg.directory filename_template = cfg.filename_template + jpeg_ext = cfg.jpeg_ext strip = cfg.strip edited_suffix = cfg.edited_suffix original_suffix = cfg.original_suffix @@ -2265,6 +2278,7 @@ def export( use_photokit=use_photokit, exiftool_option=exiftool_option, strip=strip, + jpeg_ext=jpeg_ext, ) results += export_results @@ -2839,6 +2853,7 @@ def export_photo( use_photokit=False, exiftool_option=None, strip=False, + jpeg_ext=None, ): """Helper function for export that does the actual export @@ -2876,6 +2891,7 @@ def export_photo( exiftool_option: optional list flags (e.g. ["-m", "-F"]) to pass to exiftool exiftool_merge_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool) exiftool_merge_persons: boolean; if True, merged persons found in file's exif data (requires exiftool) + jpeg_ext: if not None, specify the extension to use for all JPEG images on export Returns: list of path(s) of exported photo or None if photo was missing @@ -2933,6 +2949,7 @@ def export_photo( photo, filename_template, original_name, strip=strip ) for filename in filenames: + rendered_suffix = "" if original_suffix: try: rendered_suffix, unmatched = photo.render_template( @@ -2955,14 +2972,17 @@ def export_photo( ) rendered_suffix = rendered_suffix[0] - original_filename = pathlib.Path(filename) - original_filename = ( - original_filename.parent - / f"{original_filename.stem}{rendered_suffix}{original_filename.suffix}" - ) - original_filename = str(original_filename) - else: - original_filename = filename + original_filename = pathlib.Path(filename) + file_ext = ( + "." + jpeg_ext + if jpeg_ext and photo.uti == "public.jpeg" + else original_filename.suffix + ) + original_filename = ( + original_filename.parent + / f"{original_filename.stem}{rendered_suffix}{file_ext}" + ) + original_filename = str(original_filename) verbose_( f"Exporting {photo.original_filename} ({photo.filename}) as {original_filename}" @@ -3046,6 +3066,7 @@ def export_photo( use_photokit=use_photokit, verbose=verbose_, exiftool_flags=exiftool_option, + jpeg_ext=jpeg_ext, ) results += export_results for warning_ in export_results.exiftool_warning: @@ -3087,13 +3108,15 @@ def export_photo( # verify the photo has adjustments and valid path to avoid raising an exception if export_edited and photo.hasadjustments: edited_filename = pathlib.Path(filename) - # check for correct edited suffix - if photo.path_edited is not None: - edited_ext = pathlib.Path(photo.path_edited).suffix - else: - # use filename suffix which might be wrong, - # will be corrected by use_photos_export - edited_ext = pathlib.Path(photo.filename).suffix + edited_ext = ( + "." + jpeg_ext + if jpeg_ext and photo.uti_edited == "public.jpeg" + else "." + get_preferred_uti_extension(photo.uti_edited) + if photo.uti_edited + else pathlib.Path(photo.path_edited).suffix + if photo.path_edited + else pathlib.Path(photo.filename).suffix + ) if edited_suffix: try: @@ -3128,7 +3151,9 @@ def export_photo( ) if missing_edited: space = " " if not verbose else "" - verbose_(f"{space}Skipping missing edited photo for {edited_filename}") + verbose_( + f"{space}Skipping missing edited photo for {edited_filename}" + ) results.missing.append( str(pathlib.Path(dest_path) / edited_filename) ) @@ -3140,7 +3165,7 @@ def export_photo( f"{space}Skipping missing deleted photo {photo.original_filename} ({photo.uuid})" ) results.missing.append( - str(pathlib.Path(dest_path) / edited_filename ) + str(pathlib.Path(dest_path) / edited_filename) ) else: @@ -3173,6 +3198,7 @@ def export_photo( use_photokit=use_photokit, verbose=verbose_, exiftool_flags=exiftool_option, + jpeg_ext=jpeg_ext, ) results += export_results_edited for warning_ in export_results_edited.exiftool_warning: diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 457cc211..bca13460 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,5 +1,3 @@ """ version info """ -__version__ = "0.39.13" - - +__version__ = "0.39.14" diff --git a/osxphotos/photoinfo/_photoinfo_export.py b/osxphotos/photoinfo/_photoinfo_export.py index 366c8b8d..60bcd1d2 100644 --- a/osxphotos/photoinfo/_photoinfo_export.py +++ b/osxphotos/photoinfo/_photoinfo_export.py @@ -437,6 +437,7 @@ def export2( exiftool_flags=None, merge_exif_keywords=False, merge_exif_persons=False, + jpeg_ext=None, ): """export photo, like export but with update and dry_run options dest: must be valid destination path or exception raised @@ -488,6 +489,7 @@ def export2( exiftool_flags: optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"] merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool) merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool) + jpeg_ext: 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 "." Returns: ExportResults class ExportResults has attributes: @@ -576,7 +578,8 @@ def export2( if convert_to_jpeg and self.isphoto and uti != "public.jpeg": # not a jpeg but will convert to jpeg upon export so fix file extension fname_new = pathlib.Path(fname) - fname = str(fname_new.parent / f"{fname_new.stem}.jpeg") + ext = "." + jpeg_ext if jpeg_ext else ".jpeg" + fname = str(fname_new.parent / f"{fname_new.stem}{ext}") else: # nothing to convert convert_to_jpeg = False diff --git a/tests/search_info_test_data_10_15_7.json b/tests/search_info_test_data_10_15_7.json index 8718d90f..d73b38ed 100644 --- a/tests/search_info_test_data_10_15_7.json +++ b/tests/search_info_test_data_10_15_7.json @@ -1 +1 @@ -{"UUID_SEARCH_INFO": {"C8EAF50A-D891-4E0C-8086-C417E1284153": {"labels": ["Food", "Butter"], "place_names": ["Durham Bulls Athletic Park"], "streets": ["Blackwell St"], "neighborhoods": ["American Tobacco District", "Downtown Durham"], "city": "Durham", "locality_names": ["Durham"], "state": "North Carolina", "state_abbreviation": "NC", "country": "United States", "bodies_of_water": [], "month": "October", "year": "2018", "holidays": [], "activities": ["Entertainment", "Travel", "Dining", "Dinner", "Trip"], "season": "Fall", "venues": ["Copa", "Luna Rotisserie and Empanadas", "The Pinhook", "Pie Pusher's"], "venue_types": [], "media_types": []}, "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": {"labels": ["Desert", "Land", "Outdoor", "Sky", "Sunset Sunrise"], "place_names": ["Royal Palms State Beach"], "streets": [], "neighborhoods": ["San Pedro"], "city": "Los Angeles", "locality_names": [], "state": "California", "state_abbreviation": "", "country": "United States", "bodies_of_water": ["Catalina Channel"], "month": "November", "year": "2017", "holidays": [], "activities": ["Beach Activity", "Activity"], "season": "Fall", "venues": [], "venue_types": [], "media_types": ["Live Photos"]}, "2C151013-5BBA-4D00-B70F-1C9420418B86": {"labels": ["Land", "Water Body", "Furniture", "Bench", "Water", "People", "Forest", "Vegetation", "Outdoor"], "place_names": [], "streets": [], "neighborhoods": [], "city": "", "locality_names": [], "state": "", "state_abbreviation": "", "country": "", "bodies_of_water": [], "month": "December", "year": "2014", "holidays": ["Christmas Day"], "activities": ["Celebration", "Holiday"], "season": "Winter", "venues": [], "venue_types": [], "media_types": []}}, "UUID_SEARCH_INFO_NORMALIZED": {"C8EAF50A-D891-4E0C-8086-C417E1284153": {"labels": ["food", "butter"], "place_names": ["durham bulls athletic park"], "streets": ["blackwell st"], "neighborhoods": ["american tobacco district", "downtown durham"], "city": "durham", "locality_names": ["durham"], "state": "north carolina", "state_abbreviation": "nc", "country": "united states", "bodies_of_water": [], "month": "october", "year": "2018", "holidays": [], "activities": ["entertainment", "travel", "dining", "dinner", "trip"], "season": "fall", "venues": ["copa", "luna rotisserie and empanadas", "the pinhook", "pie pusher's"], "venue_types": [], "media_types": []}, "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": {"labels": ["desert", "land", "outdoor", "sky", "sunset sunrise"], "place_names": ["royal palms state beach"], "streets": [], "neighborhoods": ["san pedro"], "city": "los angeles", "locality_names": [], "state": "california", "state_abbreviation": "", "country": "united states", "bodies_of_water": ["catalina channel"], "month": "november", "year": "2017", "holidays": [], "activities": ["beach activity", "activity"], "season": "fall", "venues": [], "venue_types": [], "media_types": ["live photos"]}, "2C151013-5BBA-4D00-B70F-1C9420418B86": {"labels": ["land", "water body", "furniture", "bench", "water", "people", "forest", "vegetation", "outdoor"], "place_names": [], "streets": [], "neighborhoods": [], "city": "", "locality_names": [], "state": "", "state_abbreviation": "", "country": "", "bodies_of_water": [], "month": "december", "year": "2014", "holidays": ["christmas day"], "activities": ["celebration", "holiday"], "season": "winter", "venues": [], "venue_types": [], "media_types": []}}, "UUID_SEARCH_INFO_ALL": {"C8EAF50A-D891-4E0C-8086-C417E1284153": ["Food", "Butter", "Durham Bulls Athletic Park", "Blackwell St", "American Tobacco District", "Downtown Durham", "Durham", "Entertainment", "Travel", "Dining", "Dinner", "Trip", "Copa", "Luna Rotisserie and Empanadas", "The Pinhook", "Pie Pusher's", "Durham", "North Carolina", "NC", "United States", "October", "2018", "Fall"], "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": ["Desert", "Land", "Outdoor", "Sky", "Sunset Sunrise", "Royal Palms State Beach", "San Pedro", "Catalina Channel", "Beach Activity", "Activity", "Live Photos", "Los Angeles", "California", "United States", "November", "2017", "Fall"], "2C151013-5BBA-4D00-B70F-1C9420418B86": ["Land", "Water Body", "Furniture", "Bench", "Water", "People", "Forest", "Vegetation", "Outdoor", "Christmas Day", "Celebration", "Holiday", "December", "2014", "Winter"]}, "UUID_SEARCH_INFO_ALL_NORMALIZED": {"C8EAF50A-D891-4E0C-8086-C417E1284153": ["food", "butter", "durham bulls athletic park", "blackwell st", "american tobacco district", "downtown durham", "durham", "entertainment", "travel", "dining", "dinner", "trip", "copa", "luna rotisserie and empanadas", "the pinhook", "pie pusher's", "durham", "north carolina", "nc", "united states", "october", "2018", "fall"], "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": ["desert", "land", "outdoor", "sky", "sunset sunrise", "royal palms state beach", "san pedro", "catalina channel", "beach activity", "activity", "live photos", "los angeles", "california", "united states", "november", "2017", "fall"], "2C151013-5BBA-4D00-B70F-1C9420418B86": ["land", "water body", "furniture", "bench", "water", "people", "forest", "vegetation", "outdoor", "christmas day", "celebration", "holiday", "december", "2014", "winter"]}} +{"UUID_SEARCH_INFO": {"C8EAF50A-D891-4E0C-8086-C417E1284153": {"labels": ["Butter", "Food"], "place_names": ["Durham Bulls Athletic Park"], "streets": ["Blackwell St"], "neighborhoods": ["American Tobacco District", "Downtown Durham"], "city": "Durham", "locality_names": ["Durham"], "state": "North Carolina", "state_abbreviation": "NC", "country": "United States", "bodies_of_water": [], "month": "October", "year": "2018", "holidays": [], "activities": ["Entertainment", "Travel", "Dining", "Dinner", "Trip"], "season": "Fall", "venues": ["Copa", "Pie Pusher's", "Luna Rotisserie and Empanadas", "The Pinhook"], "venue_types": ["Nightlife", "Cocktail Bar", "Pizza", "Restaurant", "Bar", "Tapas & Small Plates", "Food", "Empanadas", "Arts & Entertainment", "Chicken Wings", "Latin American", "Cuban", "Music Venue"], "media_types": []}, "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": {"labels": ["Desert", "Land", "Outdoor", "Sky", "Sunset Sunrise"], "place_names": ["Royal Palms State Beach"], "streets": [], "neighborhoods": ["San Pedro"], "city": "Los Angeles", "locality_names": [], "state": "California", "state_abbreviation": "", "country": "United States", "bodies_of_water": ["Catalina Channel"], "month": "November", "year": "2017", "holidays": [], "activities": ["Beach Activity", "Activity"], "season": "Fall", "venues": [], "venue_types": [], "media_types": ["Live Photos"]}, "2C151013-5BBA-4D00-B70F-1C9420418B86": {"labels": ["People", "Land", "Furniture", "Bench", "Water", "Water Body", "Plant", "Outdoor", "Vegetation", "Forest"], "place_names": [], "streets": [], "neighborhoods": [], "city": "", "locality_names": [], "state": "", "state_abbreviation": "", "country": "", "bodies_of_water": [], "month": "December", "year": "2014", "holidays": ["Christmas Day"], "activities": ["Celebration", "Holiday"], "season": "Winter", "venues": [], "venue_types": [], "media_types": []}}, "UUID_SEARCH_INFO_NORMALIZED": {"C8EAF50A-D891-4E0C-8086-C417E1284153": {"labels": ["butter", "food"], "place_names": ["durham bulls athletic park"], "streets": ["blackwell st"], "neighborhoods": ["american tobacco district", "downtown durham"], "city": "durham", "locality_names": ["durham"], "state": "north carolina", "state_abbreviation": "nc", "country": "united states", "bodies_of_water": [], "month": "october", "year": "2018", "holidays": [], "activities": ["entertainment", "travel", "dining", "dinner", "trip"], "season": "fall", "venues": ["copa", "pie pusher's", "luna rotisserie and empanadas", "the pinhook"], "venue_types": ["nightlife", "cocktail bar", "pizza", "restaurant", "bar", "tapas & small plates", "food", "empanadas", "arts & entertainment", "chicken wings", "latin american", "cuban", "music venue"], "media_types": []}, "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": {"labels": ["desert", "land", "outdoor", "sky", "sunset sunrise"], "place_names": ["royal palms state beach"], "streets": [], "neighborhoods": ["san pedro"], "city": "los angeles", "locality_names": [], "state": "california", "state_abbreviation": "", "country": "united states", "bodies_of_water": ["catalina channel"], "month": "november", "year": "2017", "holidays": [], "activities": ["beach activity", "activity"], "season": "fall", "venues": [], "venue_types": [], "media_types": ["live photos"]}, "2C151013-5BBA-4D00-B70F-1C9420418B86": {"labels": ["people", "land", "furniture", "bench", "water", "water body", "plant", "outdoor", "vegetation", "forest"], "place_names": [], "streets": [], "neighborhoods": [], "city": "", "locality_names": [], "state": "", "state_abbreviation": "", "country": "", "bodies_of_water": [], "month": "december", "year": "2014", "holidays": ["christmas day"], "activities": ["celebration", "holiday"], "season": "winter", "venues": [], "venue_types": [], "media_types": []}}, "UUID_SEARCH_INFO_ALL": {"C8EAF50A-D891-4E0C-8086-C417E1284153": ["Butter", "Food", "Durham Bulls Athletic Park", "Blackwell St", "American Tobacco District", "Downtown Durham", "Durham", "Entertainment", "Travel", "Dining", "Dinner", "Trip", "Copa", "Pie Pusher's", "Luna Rotisserie and Empanadas", "The Pinhook", "Nightlife", "Cocktail Bar", "Pizza", "Restaurant", "Bar", "Tapas & Small Plates", "Food", "Empanadas", "Arts & Entertainment", "Chicken Wings", "Latin American", "Cuban", "Music Venue", "Durham", "North Carolina", "NC", "United States", "October", "2018", "Fall"], "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": ["Desert", "Land", "Outdoor", "Sky", "Sunset Sunrise", "Royal Palms State Beach", "San Pedro", "Catalina Channel", "Beach Activity", "Activity", "Live Photos", "Los Angeles", "California", "United States", "November", "2017", "Fall"], "2C151013-5BBA-4D00-B70F-1C9420418B86": ["People", "Land", "Furniture", "Bench", "Water", "Water Body", "Plant", "Outdoor", "Vegetation", "Forest", "Christmas Day", "Celebration", "Holiday", "December", "2014", "Winter"]}, "UUID_SEARCH_INFO_ALL_NORMALIZED": {"C8EAF50A-D891-4E0C-8086-C417E1284153": ["butter", "food", "durham bulls athletic park", "blackwell st", "american tobacco district", "downtown durham", "durham", "entertainment", "travel", "dining", "dinner", "trip", "copa", "pie pusher's", "luna rotisserie and empanadas", "the pinhook", "nightlife", "cocktail bar", "pizza", "restaurant", "bar", "tapas & small plates", "food", "empanadas", "arts & entertainment", "chicken wings", "latin american", "cuban", "music venue", "durham", "north carolina", "nc", "united states", "october", "2018", "fall"], "71DFB4C3-E868-4BE4-906E-D96BD8692D7E": ["desert", "land", "outdoor", "sky", "sunset sunrise", "royal palms state beach", "san pedro", "catalina channel", "beach activity", "activity", "live photos", "los angeles", "california", "united states", "november", "2017", "fall"], "2C151013-5BBA-4D00-B70F-1C9420418B86": ["people", "land", "furniture", "bench", "water", "water body", "plant", "outdoor", "vegetation", "forest", "christmas day", "celebration", "holiday", "december", "2014", "winter"]}} diff --git a/tests/test_cli.py b/tests/test_cli.py index 0f6ea834..47df76eb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -565,6 +565,14 @@ UUID_NO_LIKES = [ "1C1C8F1F-826B-4A24-B1CB-56628946A834", ] +UUID_JPEGS_DICT = { + "4D521201-92AC-43E5-8F7C-59BC41C37A96": ["IMG_1997", "JPG"], + "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51": ["wedding", "jpg"], + "E2078879-A29C-4D6F-BACB-E3BBE6C3EB91": ["screenshot-really-a-png", "jpeg"], +} + +UUID_HEIC = {"7783E8E6-9CAC-40F3-BE22-81FB7051C266": "IMG_3092"} + def modify_file(filename): """ appends data to a file to modify it """ @@ -5238,3 +5246,77 @@ def test_export_xattr_template(): assert sorted(md.keywords) == sorted(expected) assert md.comment == CLI_FINDER_TAGS[uuid]["XMP:Title"] + +def test_export_jpeg_ext(): + """ test --jpeg-ext """ + import glob + import os + import os.path + from osxphotos.__main__ import export + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + for uuid, fileinfo in UUID_JPEGS_DICT.items(): + result = runner.invoke( + export, [os.path.join(cwd, PHOTOS_DB_15_7), ".", "-V", "--uuid", uuid] + ) + assert result.exit_code == 0 + files = glob.glob("*") + filename, ext = fileinfo + assert f"{filename}.{ext}" in files + + for jpeg_ext in ["jpg", "JPG", "jpeg", "JPEG"]: + with runner.isolated_filesystem(): + for uuid, fileinfo in UUID_JPEGS_DICT.items(): + result = runner.invoke( + export, + [ + os.path.join(cwd, PHOTOS_DB_15_7), + ".", + "-V", + "--uuid", + uuid, + "--jpeg-ext", + jpeg_ext, + ], + ) + assert result.exit_code == 0 + files = glob.glob("*") + filename, ext = fileinfo + assert f"{filename}.{jpeg_ext}" in files + + +@pytest.mark.skipif( + "OSXPHOTOS_TEST_CONVERT" not in os.environ, + reason="Skip if running in Github actions, no GPU.", +) +def test_export_jpeg_ext_convert_to_jpeg(): + """ test --jpeg-ext with --convert-to-jpeg """ + import glob + import os + import os.path + from osxphotos.__main__ import export + + runner = CliRunner() + cwd = os.getcwd() + # pylint: disable=not-context-manager + with runner.isolated_filesystem(): + for uuid, filename in UUID_HEIC.items(): + result = runner.invoke( + export, + [ + os.path.join(cwd, PHOTOS_DB_15_7), + ".", + "-V", + "--uuid", + uuid, + "--convert-to-jpeg", + "--jpeg-ext", + "jpg", + ], + ) + assert result.exit_code == 0 + files = glob.glob("*") + assert f"{filename}.jpg" in files diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 00000000..0e0d032e --- /dev/null +++ b/utils/README.md @@ -0,0 +1,18 @@ +# Utils + +These are various utilities used in my development workflow. They may or may not be useful to you if you're working on osxphotos. If using the AppleScripts to get data from Photos, I highly recommend the excellent [FastScripts](https://redsweater.com/fastscripts/) from Red Sweater Software. + +## Files + +|File | Description | +|-----|-------------| +|build_help_table.py| Builds the template substitutions table used in main README.md | +|check_uuid.py| Use with output file created by dump_photo_info.scpt to check ouput of osxphotos vs what Photos reports| +|copy_uuid_to_clipboard.applescript| Copy UUID of selected photo in Photos to the Clipboard| +|dump_photo_info.applescript| Dumps UUID and other info about every photo in Photos.app to a test file; see check_uuid.py| +|dump_photo_info.scpt| Compiled version of dump_photo_info.applescript| +|gen_face_test_data.py| Generate test data for test_faceinfo.py| +|generate_search_info_test_data.py | Create the test data needed for test_search_info_10_15_7.py| +|get_photo_info.applescript| Displays UUID and other info about selected photos, useful for debugging| +|get_photo_info.scpt| Compiled version of above| +|write_uuid_to_file.applescript| Writes the UUIDs of selected images in Photos to a text file; can generate input for --uuid-from-file| diff --git a/utils/copy_uuid_to_clipboard.applescript b/utils/copy_uuid_to_clipboard.applescript new file mode 100644 index 00000000..562ee4d7 --- /dev/null +++ b/utils/copy_uuid_to_clipboard.applescript @@ -0,0 +1,20 @@ +-- Copies UUID of selected photo to the clipboard, if more than one selection, copies uuid from the last item +-- Useful for debugging with osxphotos + + +tell application "Photos" + set uuid to "" + set theSelection to selection + repeat with theItem in theSelection + set uuid to ((id of theItem) as text) + set oldDelimiter to AppleScript's text item delimiters + set AppleScript's text item delimiters to "/" + set theTextItems to every text item of uuid + set uuid to first item of theTextItems + set AppleScript's text item delimiters to oldDelimiter + end repeat + set the clipboard to uuid + +end tell + +