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,