diff --git a/README.md b/README.md index bd88f590..87184a75 100644 --- a/README.md +++ b/README.md @@ -1732,7 +1732,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.45.9' +{osxphotos_version} The osxphotos version, e.g. '0.45.10' {osxphotos_cmd_line} The full command line used to run osxphotos The following substitutions may result in multiple values. Thus if specified for @@ -3636,7 +3636,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.45.9'| +|{osxphotos_version}|The osxphotos version, e.g. '0.45.10'| |{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 98e532c3..e0b9ae0c 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: a320d2e66b198895ef0b12b1f5934727 +config: 5bcb39b8d7265fb2c5780a64f97662ef tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js index 4e9dfbd4..ca742d37 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.45.9', + VERSION: '0.45.10', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/cli.html b/docs/cli.html index 80bf6c2b..eeed3e6b 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -6,7 +6,7 @@ - osxphotos command line interface (CLI) — osxphotos 0.45.9 documentation + osxphotos command line interface (CLI) — osxphotos 0.45.10 documentation diff --git a/docs/genindex.html b/docs/genindex.html index d42baa7d..c2a53276 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -5,7 +5,7 @@ - Index — osxphotos 0.45.9 documentation + Index — osxphotos 0.45.10 documentation diff --git a/docs/index.html b/docs/index.html index ca087b89..96259539 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,7 +6,7 @@ - Welcome to osxphotos’s documentation! — osxphotos 0.45.9 documentation + Welcome to osxphotos’s documentation! — osxphotos 0.45.10 documentation diff --git a/docs/modules.html b/docs/modules.html index 16224df4..574b0ef2 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -6,7 +6,7 @@ - osxphotos — osxphotos 0.45.9 documentation + osxphotos — osxphotos 0.45.10 documentation diff --git a/docs/reference.html b/docs/reference.html index 4a3c30f4..2a5a690f 100644 --- a/docs/reference.html +++ b/docs/reference.html @@ -6,7 +6,7 @@ - osxphotos package — osxphotos 0.45.9 documentation + osxphotos package — osxphotos 0.45.10 documentation diff --git a/docs/search.html b/docs/search.html index 8a4d69fa..c8c10173 100644 --- a/docs/search.html +++ b/docs/search.html @@ -5,7 +5,7 @@ - Search — osxphotos 0.45.9 documentation + Search — osxphotos 0.45.10 documentation diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 2a087d5e..7ab2582d 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.45.9" +__version__ = "0.45.10" diff --git a/osxphotos/cli.py b/osxphotos/cli.py index 76cc2e79..822d29e1 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -3164,7 +3164,7 @@ def export_photo_to_directory( ) if verbose: - if update: + if update or force_update: for new in results.new: verbose_(f"Exported new file {new}") for updated in results.updated: diff --git a/osxphotos/photoexporter.py b/osxphotos/photoexporter.py index 264239b6..146ac116 100644 --- a/osxphotos/photoexporter.py +++ b/osxphotos/photoexporter.py @@ -1033,9 +1033,9 @@ class PhotoExporter: # need to also check the photo's metadata to that in the database # and if anything changed, we need to update the file # ony the hex digest of the metadata is stored in the database - cmp_orig = hexdigest( - self.photo.json() - ) == export_db.get_metadata_for_file(dest_str) + photo_digest = hexdigest(self.photo.json()) + db_digest = export_db.get_metadata_for_file(dest_str) + cmp_orig = photo_digest == db_digest sig_cmp = cmp_touch if options.touch_file else cmp_orig @@ -1138,6 +1138,8 @@ class PhotoExporter: ) from e json_info = self.photo.json() + # don't set the metadata digest if not force_update so that future use of force_update catches metadata change + metadata_digest = hexdigest(json_info) if options.force_update else None export_db.set_data( filename=dest_str, uuid=self.photo.uuid, @@ -1145,7 +1147,7 @@ class PhotoExporter: converted_stat=converted_stat, edited_stat=edited_stat, info_json=json_info, - metadata=hexdigest(json_info), + metadata=metadata_digest, ) return ExportResults( diff --git a/osxphotos/photoinfo.py b/osxphotos/photoinfo.py index 17f9500f..a5b213fa 100644 --- a/osxphotos/photoinfo.py +++ b/osxphotos/photoinfo.py @@ -1729,6 +1729,9 @@ class PhotoInfo: return o.isoformat() dict_data = self.asdict() + for k, v in dict_data.items(): + if v and isinstance(v, (list, tuple)) and not isinstance(v[0], dict): + dict_data[k] = sorted(v) return json.dumps(dict_data, sort_keys=True, default=default) def __eq__(self, other): diff --git a/osxphotos/searchinfo.py b/osxphotos/searchinfo.py index 09afd2cd..ee84d9a3 100644 --- a/osxphotos/searchinfo.py +++ b/osxphotos/searchinfo.py @@ -211,10 +211,12 @@ class SearchInfo: """return list of text for a specified category ID""" if self._db_searchinfo: content = "normalized_string" if self._normalized else "content_string" - return [ - rec[content] - for rec in self._db_searchinfo - if rec["category"] == category - ] + return sorted( + [ + rec[content] + for rec in self._db_searchinfo + if rec["category"] == category + ] + ) else: return [] diff --git a/tests/test_cli.py b/tests/test_cli.py index 6efdbe2f..0a522990 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4845,15 +4845,12 @@ def test_export_force_update(): in result.output ) - # force update + # force update must be run once to set the metadata digest info + # in practice, this means that first time user uses --force-update, most files will likely be re-exported result = runner.invoke( export, [os.path.join(cwd, photos_db_path), ".", "--force-update"] ) assert result.exit_code == 0 - assert ( - f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0" - in result.output - ) # update a file dbpath = os.path.join(photos_db_path, "database/Photos.sqlite") @@ -4869,7 +4866,7 @@ def test_export_force_update(): ) conn.commit() - # run again to see if updated metadata forced update + # run --force-update to see if updated metadata forced update result = runner.invoke( export, [os.path.join(cwd, photos_db_path), ".", "--force-update"] ) @@ -4879,6 +4876,25 @@ def test_export_force_update(): in result.output ) + # update, nothing should export + result = runner.invoke( + export, [os.path.join(cwd, photos_db_path), ".", "--update"] + ) + assert result.exit_code == 0 + assert ( + f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0" + in result.output + ) + + # run --force-update, nothing should export + result = runner.invoke( + export, [os.path.join(cwd, photos_db_path), ".", "--force-update"] + ) + assert result.exit_code == 0 + assert ( + f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0" + in result.output + ) @pytest.mark.skipif( "OSXPHOTOS_TEST_EXPORT" not in os.environ,