Added --force-update, #621
This commit is contained in:
@@ -1732,7 +1732,7 @@ Substitution Description
|
|||||||
{lf} A line feed: '\n', alias for {newline}
|
{lf} A line feed: '\n', alias for {newline}
|
||||||
{cr} A carriage return: '\r'
|
{cr} A carriage return: '\r'
|
||||||
{crlf} a carriage return + line feed: '\r\n'
|
{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
|
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||||
|
|
||||||
The following substitutions may result in multiple values. Thus if specified for
|
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}|
|
|{lf}|A line feed: '\n', alias for {newline}|
|
||||||
|{cr}|A carriage return: '\r'|
|
|{cr}|A carriage return: '\r'|
|
||||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
|{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|
|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||||
|{album}|Album(s) photo is contained in|
|
|{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|
|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# 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.
|
# 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
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
|||||||
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@@ -1,6 +1,6 @@
|
|||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||||
VERSION: '0.45.9',
|
VERSION: '0.45.10',
|
||||||
LANGUAGE: 'None',
|
LANGUAGE: 'None',
|
||||||
COLLAPSE_INDEX: false,
|
COLLAPSE_INDEX: false,
|
||||||
BUILDER: 'html',
|
BUILDER: 'html',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||||||
|
|
||||||
<title>osxphotos command line interface (CLI) — osxphotos 0.45.9 documentation</title>
|
<title>osxphotos command line interface (CLI) — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Index — osxphotos 0.45.9 documentation</title>
|
<title>Index — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||||||
|
|
||||||
<title>Welcome to osxphotos’s documentation! — osxphotos 0.45.9 documentation</title>
|
<title>Welcome to osxphotos’s documentation! — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||||||
|
|
||||||
<title>osxphotos — osxphotos 0.45.9 documentation</title>
|
<title>osxphotos — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||||||
|
|
||||||
<title>osxphotos package — osxphotos 0.45.9 documentation</title>
|
<title>osxphotos package — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Search — osxphotos 0.45.9 documentation</title>
|
<title>Search — osxphotos 0.45.10 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.45.9"
|
__version__ = "0.45.10"
|
||||||
|
|||||||
@@ -3164,7 +3164,7 @@ def export_photo_to_directory(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
if update:
|
if update or force_update:
|
||||||
for new in results.new:
|
for new in results.new:
|
||||||
verbose_(f"Exported new file {new}")
|
verbose_(f"Exported new file {new}")
|
||||||
for updated in results.updated:
|
for updated in results.updated:
|
||||||
|
|||||||
@@ -1033,9 +1033,9 @@ class PhotoExporter:
|
|||||||
# need to also check the photo's metadata to that in the database
|
# need to also check the photo's metadata to that in the database
|
||||||
# and if anything changed, we need to update the file
|
# and if anything changed, we need to update the file
|
||||||
# ony the hex digest of the metadata is stored in the database
|
# ony the hex digest of the metadata is stored in the database
|
||||||
cmp_orig = hexdigest(
|
photo_digest = hexdigest(self.photo.json())
|
||||||
self.photo.json()
|
db_digest = export_db.get_metadata_for_file(dest_str)
|
||||||
) == 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
|
sig_cmp = cmp_touch if options.touch_file else cmp_orig
|
||||||
|
|
||||||
@@ -1138,6 +1138,8 @@ class PhotoExporter:
|
|||||||
) from e
|
) from e
|
||||||
|
|
||||||
json_info = self.photo.json()
|
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(
|
export_db.set_data(
|
||||||
filename=dest_str,
|
filename=dest_str,
|
||||||
uuid=self.photo.uuid,
|
uuid=self.photo.uuid,
|
||||||
@@ -1145,7 +1147,7 @@ class PhotoExporter:
|
|||||||
converted_stat=converted_stat,
|
converted_stat=converted_stat,
|
||||||
edited_stat=edited_stat,
|
edited_stat=edited_stat,
|
||||||
info_json=json_info,
|
info_json=json_info,
|
||||||
metadata=hexdigest(json_info),
|
metadata=metadata_digest,
|
||||||
)
|
)
|
||||||
|
|
||||||
return ExportResults(
|
return ExportResults(
|
||||||
|
|||||||
@@ -1729,6 +1729,9 @@ class PhotoInfo:
|
|||||||
return o.isoformat()
|
return o.isoformat()
|
||||||
|
|
||||||
dict_data = self.asdict()
|
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)
|
return json.dumps(dict_data, sort_keys=True, default=default)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
|||||||
@@ -211,10 +211,12 @@ class SearchInfo:
|
|||||||
"""return list of text for a specified category ID"""
|
"""return list of text for a specified category ID"""
|
||||||
if self._db_searchinfo:
|
if self._db_searchinfo:
|
||||||
content = "normalized_string" if self._normalized else "content_string"
|
content = "normalized_string" if self._normalized else "content_string"
|
||||||
return [
|
return sorted(
|
||||||
rec[content]
|
[
|
||||||
for rec in self._db_searchinfo
|
rec[content]
|
||||||
if rec["category"] == category
|
for rec in self._db_searchinfo
|
||||||
]
|
if rec["category"] == category
|
||||||
|
]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -4845,15 +4845,12 @@ def test_export_force_update():
|
|||||||
in result.output
|
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(
|
result = runner.invoke(
|
||||||
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
|
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
|
||||||
)
|
)
|
||||||
assert result.exit_code == 0
|
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
|
# update a file
|
||||||
dbpath = os.path.join(photos_db_path, "database/Photos.sqlite")
|
dbpath = os.path.join(photos_db_path, "database/Photos.sqlite")
|
||||||
@@ -4869,7 +4866,7 @@ def test_export_force_update():
|
|||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
# run again to see if updated metadata forced update
|
# run --force-update to see if updated metadata forced update
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
|
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
|
||||||
)
|
)
|
||||||
@@ -4879,6 +4876,25 @@ def test_export_force_update():
|
|||||||
in result.output
|
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(
|
@pytest.mark.skipif(
|
||||||
"OSXPHOTOS_TEST_EXPORT" not in os.environ,
|
"OSXPHOTOS_TEST_EXPORT" not in os.environ,
|
||||||
|
|||||||
Reference in New Issue
Block a user