From 39ba17dd1cb4d8a61ab4dc8d5cff12ff9871eee0 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Tue, 22 Feb 2022 06:40:25 -0800 Subject: [PATCH] Added debug output to exiftool --- README.md | 4 +- docs/.buildinfo | 2 +- docs/_modules/index.html | 2 +- .../_modules/osxphotos/photosdb/photosdb.html | 46 ++++++++-------- docs/_static/documentation_options.js | 2 +- docs/cli.html | 2 +- docs/genindex.html | 2 +- docs/index.html | 2 +- docs/modules.html | 2 +- docs/reference.html | 2 +- docs/search.html | 2 +- osxphotos/_version.py | 2 +- osxphotos/cli.py | 9 ++++ osxphotos/exiftool.py | 54 ++++++++++++++++--- osxphotos/photoinfo.py | 4 +- 15 files changed, 92 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index facf49ae..78707097 100644 --- a/README.md +++ b/README.md @@ -1741,7 +1741,7 @@ Substitution Description {lf} A line feed: '\n', alias for {newline} {cr} A carriage return: '\r' {crlf} a carriage return + line feed: '\r\n' -{osxphotos_version} The osxphotos version, e.g. '0.46.1' +{osxphotos_version} The osxphotos version, e.g. '0.46.2' {osxphotos_cmd_line} The full command line used to run osxphotos The following substitutions may result in multiple values. Thus if specified for @@ -3645,7 +3645,7 @@ The following template field substitutions are availabe for use the templating s |{lf}|A line feed: '\n', alias for {newline}| |{cr}|A carriage return: '\r'| |{crlf}|a carriage return + line feed: '\r\n'| -|{osxphotos_version}|The osxphotos version, e.g. '0.46.1'| +|{osxphotos_version}|The osxphotos version, e.g. '0.46.2'| |{osxphotos_cmd_line}|The full command line used to run osxphotos| |{album}|Album(s) photo is contained in| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| diff --git a/docs/.buildinfo b/docs/.buildinfo index 18882184..be2e0bc2 100644 --- a/docs/.buildinfo +++ b/docs/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: d6da9902a4771e5081ae73c361960af8 +config: 3bdc7daae06c46fa0e6357e497a27088 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_modules/index.html b/docs/_modules/index.html index 9d8e91a1..93caf782 100644 --- a/docs/_modules/index.html +++ b/docs/_modules/index.html @@ -5,7 +5,7 @@ - Overview: module code — osxphotos 0.46.1 documentation + Overview: module code — osxphotos 0.46.2 documentation diff --git a/docs/_modules/osxphotos/photosdb/photosdb.html b/docs/_modules/osxphotos/photosdb/photosdb.html index fa076fe6..4ac4adc2 100644 --- a/docs/_modules/osxphotos/photosdb/photosdb.html +++ b/docs/_modules/osxphotos/photosdb/photosdb.html @@ -5,7 +5,7 @@ - osxphotos.photosdb.photosdb — osxphotos 0.46.0 documentation + osxphotos.photosdb.photosdb — osxphotos 0.46.2 documentation @@ -3312,27 +3312,6 @@ if options.to_time: photos = [p for p in photos if p.date.time() <= options.to_time] - if options.burst_photos: - # add the burst_photos to the export set - photos_burst = [p for p in photos if p.burst] - for burst in photos_burst: - if options.missing_bursts: - # include burst photos that are missing - photos.extend(burst.burst_photos) - else: - # don't include missing burst images (these can't be downloaded with AppleScript) - photos.extend([p for p in burst.burst_photos if not p.ismissing]) - - # remove duplicates as each burst photo in the set that's selected would - # result in the entire set being added above - # can't use set() because PhotoInfo not hashable - seen_uuids = {} - for p in photos: - if p.uuid in seen_uuids: - continue - 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 @@ -3483,6 +3462,28 @@ for function in options.function: photos = function[0](photos) + # burst should be checked last, ref #640 + if options.burst_photos: + # add the burst_photos to the export set + photos_burst = [p for p in photos if p.burst] + for burst in photos_burst: + if options.missing_bursts: + # include burst photos that are missing + photos.extend(burst.burst_photos) + else: + # don't include missing burst images (these can't be downloaded with AppleScript) + photos.extend([p for p in burst.burst_photos if not p.ismissing]) + + # remove duplicates as each burst photo in the set that's selected would + # result in the entire set being added above + # can't use set() because PhotoInfo not hashable + seen_uuids = {} + for p in photos: + if p.uuid in seen_uuids: + continue + seen_uuids[p.uuid] = p + photos = list(seen_uuids.values()) + return photos
[docs] def execute(self, sql): @@ -3568,7 +3569,6 @@

Navigation

diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js index 3f840ca4..c04ab558 100644 --- a/docs/_static/documentation_options.js +++ b/docs/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.46.1', + VERSION: '0.46.2', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/cli.html b/docs/cli.html index 6679d4a5..3de936ee 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -6,7 +6,7 @@ - osxphotos command line interface (CLI) — osxphotos 0.46.1 documentation + osxphotos command line interface (CLI) — osxphotos 0.46.2 documentation diff --git a/docs/genindex.html b/docs/genindex.html index ffe2a1aa..f5d8a9c3 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -5,7 +5,7 @@ - Index — osxphotos 0.46.1 documentation + Index — osxphotos 0.46.2 documentation diff --git a/docs/index.html b/docs/index.html index c35a8ef8..83a91bd0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,7 +6,7 @@ - Welcome to osxphotos’s documentation! — osxphotos 0.46.1 documentation + Welcome to osxphotos’s documentation! — osxphotos 0.46.2 documentation diff --git a/docs/modules.html b/docs/modules.html index 7150b67e..3d081bb4 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -6,7 +6,7 @@ - osxphotos — osxphotos 0.46.1 documentation + osxphotos — osxphotos 0.46.2 documentation diff --git a/docs/reference.html b/docs/reference.html index 179d3e76..8e55a25b 100644 --- a/docs/reference.html +++ b/docs/reference.html @@ -6,7 +6,7 @@ - osxphotos package — osxphotos 0.46.1 documentation + osxphotos package — osxphotos 0.46.2 documentation diff --git a/docs/search.html b/docs/search.html index 6e9a0dea..87df08e6 100644 --- a/docs/search.html +++ b/docs/search.html @@ -5,7 +5,7 @@ - Search — osxphotos 0.46.1 documentation + Search — osxphotos 0.46.2 documentation diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 1a86eb42..c58902c6 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.46.2" +__version__ = "0.46.3" diff --git a/osxphotos/cli.py b/osxphotos/cli.py index 0b9b99a1..b87c6b00 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -2298,6 +2298,9 @@ def help(ctx, topic, **kw): "This only works if the Photos library being queried is the last-opened (default) library in Photos. " "This feature is currently experimental. I don't know how well it will work on large query sets.", ) +@click.option( + "--debug", required=False, is_flag=True, default=False, hidden=OSXPHOTOS_HIDDEN +) @DB_ARGUMENT @click.pass_obj @click.pass_context @@ -2382,12 +2385,18 @@ def query( query_eval, query_function, add_to_album, + debug, ): """Query the Photos database using 1 or more search options; if more than one option is provided, they are treated as "AND" (e.g. search for photos matching all options). """ + global DEBUG + if debug: + DEBUG = True + osxphotos._set_debug(True) + # if no query terms, show help and return # sanity check input args nonexclusive = [ diff --git a/osxphotos/exiftool.py b/osxphotos/exiftool.py index 41be1f33..9fc8dae2 100644 --- a/osxphotos/exiftool.py +++ b/osxphotos/exiftool.py @@ -103,9 +103,11 @@ class _ExifToolProc: return cls.instance - def __init__(self, exiftool=None): + def __init__(self, exiftool=None, debug=False): """construct _ExifToolProc singleton object or return instance of already created object - exiftool: optional path to exiftool binary (if not provided, will search path to find it)""" + exiftool: optional path to exiftool binary (if not provided, will search path to find it) + debug: optional bool to enable debugging output + """ if hasattr(self, "_process_running") and self._process_running: # already running @@ -115,9 +117,11 @@ class _ExifToolProc: f"ignoring exiftool={exiftool}" ) return - + self.debug = debug self._process_running = False self._exiftool = exiftool or get_exiftool_path() + if self.debug: + logging.debug(f"exiftool={self._exiftool}") self._start_proc() @property @@ -167,10 +171,17 @@ class _ExifToolProc: self._process_running = True EXIFTOOL_PROCESSES.append(self) + if self.debug: + logging.debug( + "exiftool process started: {self._process} {self._process_running}" + ) def _stop_proc(self): """stop the exiftool process if it's running, otherwise, do nothing""" + if self.debug: + logging.debug(f"exiftool process stopping: {self._process}") + if not self._process_running: return @@ -190,11 +201,16 @@ class _ExifToolProc: del self._process self._process_running = False + if self.debug: + logging.debug(f"exiftool process stopped: {self._process}") + class ExifTool: """Basic exiftool interface for reading and writing EXIF tags""" - def __init__(self, filepath, exiftool=None, overwrite=True, flags=None): + def __init__( + self, filepath, exiftool=None, overwrite=True, flags=None, debug=False + ): """Create ExifTool object Args: @@ -202,6 +218,7 @@ class ExifTool: exiftool: path to exiftool, if not specified will look in path overwrite: if True, will overwrite image file without creating backup, default=False flags: optional list of exiftool flags to prepend to exiftool command when writing metadata (e.g. -m or -F) + debug: if True, enables debug output Returns: ExifTool instance @@ -209,6 +226,7 @@ class ExifTool: self.file = filepath self.overwrite = overwrite self.flags = flags or [] + self.debug = debug self.data = {} self.warning = None self.error = None @@ -342,6 +360,9 @@ class ExifTool: + b"-execute\n" ) + if self.debug: + logging.debug(f"running exiftool command: {command_str}") + # send the command self._process.stdin.write(command_str) self._process.stdin.flush() @@ -362,6 +383,11 @@ class ExifTool: error = "" if error == b"" else error.decode("utf-8") self.warning = warning self.error = error + if self.debug: + logging.debug( + f"run_commands: output={output[:-EXIFTOOL_STAYOPEN_EOF_LEN]}, warning={warning}, error={error}" + ) + return output[:-EXIFTOOL_STAYOPEN_EOF_LEN], warning, error @property @@ -406,18 +432,25 @@ class ExifTool: if normalized: exifdict = {k.lower(): v for (k, v) in exifdict.items()} + if self.debug: + logging.debug(f"asdict: {exifdict}") + return exifdict def json(self): """returns JSON string containing all EXIF tags and values from exiftool""" json, _, _ = self.run_commands("-json") json = unescape_str(json.decode("utf-8")) + if self.debug: + logging.debug(f"json: {json}") return json def _read_exif(self): """read exif data from file""" data = self.asdict() self.data = {k: v for k, v in data.items()} + if self.debug: + logging.debug(f"_read_exif: {self.data}") def __str__(self): return f"file: {self.file}\nexiftool: {self._exiftoolproc._exiftool}" @@ -443,15 +476,17 @@ class ExifToolCaching(ExifTool): _singletons = {} - def __new__(cls, filepath, exiftool=None): + def __new__(cls, filepath, exiftool=None, debug=False): """create new object or return instance of already created singleton""" if filepath not in cls._singletons: - cls._singletons[filepath] = _ExifToolCaching(filepath, exiftool=exiftool) + cls._singletons[filepath] = _ExifToolCaching( + filepath, exiftool=exiftool, debug=debug + ) return cls._singletons[filepath] class _ExifToolCaching(ExifTool): - def __init__(self, filepath, exiftool=None): + def __init__(self, filepath, exiftool=None, debug=False): """Create read-only ExifTool object that caches values Args: @@ -461,9 +496,12 @@ class _ExifToolCaching(ExifTool): Returns: ExifTool instance """ + self.debug = debug self._json_cache = None self._asdict_cache = {} - super().__init__(filepath, exiftool=exiftool, overwrite=False, flags=None) + super().__init__( + filepath, exiftool=exiftool, overwrite=False, flags=None, debug=debug + ) def run_commands(self, *commands, no_file=False): if commands[0] not in ["-json", "-ver"]: diff --git a/osxphotos/photoinfo.py b/osxphotos/photoinfo.py index 22976c25..798fcf9c 100644 --- a/osxphotos/photoinfo.py +++ b/osxphotos/photoinfo.py @@ -54,7 +54,7 @@ from .scoreinfo import ScoreInfo from .searchinfo import SearchInfo from .text_detection import detect_text from .uti import get_preferred_uti_extension, get_uti_for_extension -from .utils import _debug, _get_resource_loc, list_directory +from .utils import _debug, _get_resource_loc, list_directory, _debug __all__ = ["PhotoInfo", "PhotoInfoNone"] @@ -1343,7 +1343,7 @@ class PhotoInfo: try: exiftool_path = self._db._exiftool_path or get_exiftool_path() if self.path is not None and os.path.isfile(self.path): - exiftool = ExifToolCaching(self.path, exiftool=exiftool_path) + exiftool = ExifToolCaching(self.path, exiftool=exiftool_path, debug=_debug()) else: exiftool = None except FileNotFoundError: