Finished --touch-file, closes #206
This commit is contained in:
18
README.md
18
README.md
@@ -203,14 +203,14 @@ Options:
|
|||||||
both images and movies).
|
both images and movies).
|
||||||
--only-photos Search only for photos/images (default
|
--only-photos Search only for photos/images (default
|
||||||
searches both images and movies).
|
searches both images and movies).
|
||||||
--from-date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]
|
--from-date DATETIME Search by start item date, e.g.
|
||||||
Search by start item date, e.g.
|
2000-01-12T12:00:00,
|
||||||
2000-01-12T12:00:00 or 2000-12-31 (ISO 8601
|
2001-01-12T12:00:00-07:00, or 2000-12-31
|
||||||
w/o TZ).
|
(ISO 8601).
|
||||||
--to-date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]
|
--to-date DATETIME Search by end item date, e.g.
|
||||||
Search by end item date, e.g.
|
2000-01-12T12:00:00,
|
||||||
2000-01-12T12:00:00 or 2000-12-31 (ISO 8601
|
2001-01-12T12:00:00-07:00, or 2000-12-31
|
||||||
w/o TZ).
|
(ISO 8601).
|
||||||
--deleted Include photos from the 'Recently Deleted'
|
--deleted Include photos from the 'Recently Deleted'
|
||||||
folder.
|
folder.
|
||||||
--deleted-only Include only photos from the 'Recently
|
--deleted-only Include only photos from the 'Recently
|
||||||
@@ -222,6 +222,8 @@ Options:
|
|||||||
--export-as-hardlink Hardlink files instead of copying them.
|
--export-as-hardlink Hardlink files instead of copying them.
|
||||||
Cannot be used with --exiftool which creates
|
Cannot be used with --exiftool which creates
|
||||||
copies of the files with embedded EXIF data.
|
copies of the files with embedded EXIF data.
|
||||||
|
--touch-file Sets the file's modification time to match
|
||||||
|
photo date.
|
||||||
--overwrite Overwrite existing files. Default behavior
|
--overwrite Overwrite existing files. Default behavior
|
||||||
is to add (1), (2), etc to filename if file
|
is to add (1), (2), etc to filename if file
|
||||||
already exists. Use this with caution as it
|
already exists. Use this with caution as it
|
||||||
|
|||||||
@@ -1124,7 +1124,7 @@ def query(
|
|||||||
@click.option(
|
@click.option(
|
||||||
"--touch-file",
|
"--touch-file",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Sets the file's modification time upon photo date.",
|
help="Sets the file's modification time to match photo date.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--overwrite",
|
"--overwrite",
|
||||||
@@ -1269,6 +1269,13 @@ def query(
|
|||||||
"to a filesystem that doesn't support Mac OS extended attributes. Only use this if you get "
|
"to a filesystem that doesn't support Mac OS extended attributes. Only use this if you get "
|
||||||
"an error while exporting.",
|
"an error while exporting.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--use-photos-export",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
hidden=True,
|
||||||
|
help="Force the use of AppleScript to export even if not missing (see also --download-missing).",
|
||||||
|
)
|
||||||
@DB_ARGUMENT
|
@DB_ARGUMENT
|
||||||
@click.argument("dest", nargs=1, type=click.Path(exists=True))
|
@click.argument("dest", nargs=1, type=click.Path(exists=True))
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
@@ -1350,6 +1357,7 @@ def export(
|
|||||||
label,
|
label,
|
||||||
deleted,
|
deleted,
|
||||||
deleted_only,
|
deleted_only,
|
||||||
|
use_photos_export,
|
||||||
):
|
):
|
||||||
""" Export photos from the Photos database.
|
""" Export photos from the Photos database.
|
||||||
Export path DEST is required.
|
Export path DEST is required.
|
||||||
@@ -1520,7 +1528,6 @@ def export(
|
|||||||
deleted_only=deleted_only,
|
deleted_only=deleted_only,
|
||||||
)
|
)
|
||||||
|
|
||||||
results_exported = []
|
|
||||||
if photos:
|
if photos:
|
||||||
if export_bursts:
|
if export_bursts:
|
||||||
# add the burst_photos to the export set
|
# add the burst_photos to the export set
|
||||||
@@ -1539,6 +1546,7 @@ def export(
|
|||||||
# because the original code used --original-name as an option
|
# because the original code used --original-name as an option
|
||||||
original_name = not current_name
|
original_name = not current_name
|
||||||
|
|
||||||
|
results_exported = []
|
||||||
results_new = []
|
results_new = []
|
||||||
results_updated = []
|
results_updated = []
|
||||||
results_skipped = []
|
results_skipped = []
|
||||||
@@ -1573,6 +1581,7 @@ def export(
|
|||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
touch_file=touch_file,
|
touch_file=touch_file,
|
||||||
edited_suffix=edited_suffix,
|
edited_suffix=edited_suffix,
|
||||||
|
use_photos_export=use_photos_export,
|
||||||
)
|
)
|
||||||
results_exported.extend(results.exported)
|
results_exported.extend(results.exported)
|
||||||
results_new.extend(results.new)
|
results_new.extend(results.new)
|
||||||
@@ -1612,6 +1621,7 @@ def export(
|
|||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
touch_file=touch_file,
|
touch_file=touch_file,
|
||||||
edited_suffix=edited_suffix,
|
edited_suffix=edited_suffix,
|
||||||
|
use_photos_export=use_photos_export,
|
||||||
)
|
)
|
||||||
results_exported.extend(results.exported)
|
results_exported.extend(results.exported)
|
||||||
results_new.extend(results.new)
|
results_new.extend(results.new)
|
||||||
@@ -1629,10 +1639,12 @@ def export(
|
|||||||
photo_str_exif_updated = (
|
photo_str_exif_updated = (
|
||||||
"photos" if len(results_exif_updated) != 1 else "photo"
|
"photos" if len(results_exif_updated) != 1 else "photo"
|
||||||
)
|
)
|
||||||
summary = f"Exported: {len(results_new)} {photo_str_new}, " \
|
summary = (
|
||||||
f"updated: {len(results_updated)} {photo_str_updated}, " \
|
f"Exported: {len(results_new)} {photo_str_new}, "
|
||||||
f"skipped: {len(results_skipped)} {photo_str_skipped}, " \
|
f"updated: {len(results_updated)} {photo_str_updated}, "
|
||||||
f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}"
|
f"skipped: {len(results_skipped)} {photo_str_skipped}, "
|
||||||
|
f"updated EXIF data: {len(results_exif_updated)} {photo_str_exif_updated}"
|
||||||
|
)
|
||||||
if touch_file:
|
if touch_file:
|
||||||
summary += f", touched date: {len(results_touched)} {photo_str_touched}"
|
summary += f", touched date: {len(results_touched)} {photo_str_touched}"
|
||||||
click.echo(summary)
|
click.echo(summary)
|
||||||
@@ -2092,6 +2104,7 @@ def export_photo(
|
|||||||
dry_run=None,
|
dry_run=None,
|
||||||
touch_file=None,
|
touch_file=None,
|
||||||
edited_suffix="_edited",
|
edited_suffix="_edited",
|
||||||
|
use_photos_export=False,
|
||||||
):
|
):
|
||||||
""" Helper function for export that does the actual export
|
""" Helper function for export that does the actual export
|
||||||
|
|
||||||
@@ -2119,7 +2132,8 @@ def export_photo(
|
|||||||
export_db: export database instance compatible with ExportDB_ABC
|
export_db: export database instance compatible with ExportDB_ABC
|
||||||
fileutil: file util class compatible with FileUtilABC
|
fileutil: file util class compatible with FileUtilABC
|
||||||
dry_run: boolean; if True, doesn't actually export or update any files
|
dry_run: boolean; if True, doesn't actually export or update any files
|
||||||
touch_file: boolean; sets file's modification time upon photo date
|
touch_file: boolean; sets file's modification time to match photo date
|
||||||
|
use_photos_export: boolean; if True forces the use of AppleScript to export even if photo not missing
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list of path(s) of exported photo or None if photo was missing
|
list of path(s) of exported photo or None if photo was missing
|
||||||
@@ -2172,8 +2186,10 @@ def export_photo(
|
|||||||
|
|
||||||
# if download_missing and the photo is missing or path doesn't exist,
|
# if download_missing and the photo is missing or path doesn't exist,
|
||||||
# try to download with Photos
|
# try to download with Photos
|
||||||
use_photos_export = download_missing and (
|
use_photos_export = (
|
||||||
photo.ismissing or not os.path.exists(photo.path)
|
download_missing and (photo.ismissing or not os.path.exists(photo.path))
|
||||||
|
if not use_photos_export
|
||||||
|
else True
|
||||||
)
|
)
|
||||||
|
|
||||||
# export the photo to each path in dest_paths
|
# export the photo to each path in dest_paths
|
||||||
@@ -2225,7 +2241,6 @@ def export_photo(
|
|||||||
if export_edited and photo.hasadjustments:
|
if export_edited and photo.hasadjustments:
|
||||||
# if download_missing and the photo is missing or path doesn't exist,
|
# if download_missing and the photo is missing or path doesn't exist,
|
||||||
# try to download with Photos
|
# try to download with Photos
|
||||||
use_photos_export = download_missing and photo.path_edited is None
|
|
||||||
if not download_missing and photo.path_edited is None:
|
if not download_missing and photo.path_edited is None:
|
||||||
verbose(f"Skipping missing edited photo for {filename}")
|
verbose(f"Skipping missing edited photo for {filename}")
|
||||||
else:
|
else:
|
||||||
@@ -2266,7 +2281,7 @@ def export_photo(
|
|||||||
results_updated.extend(export_results_edited.updated)
|
results_updated.extend(export_results_edited.updated)
|
||||||
results_skipped.extend(export_results_edited.skipped)
|
results_skipped.extend(export_results_edited.skipped)
|
||||||
results_exif_updated.extend(export_results_edited.exif_updated)
|
results_exif_updated.extend(export_results_edited.exif_updated)
|
||||||
results_touched.extend(export_results.touched)
|
results_touched.extend(export_results_edited.touched)
|
||||||
|
|
||||||
if verbose_:
|
if verbose_:
|
||||||
for exported in export_results_edited.exported:
|
for exported in export_results_edited.exported:
|
||||||
@@ -2277,6 +2292,8 @@ def export_photo(
|
|||||||
verbose(f"Exported updated file {updated}")
|
verbose(f"Exported updated file {updated}")
|
||||||
for skipped in export_results_edited.skipped:
|
for skipped in export_results_edited.skipped:
|
||||||
verbose(f"Skipped up to date file {skipped}")
|
verbose(f"Skipped up to date file {skipped}")
|
||||||
|
for touched in export_results_edited.touched:
|
||||||
|
verbose(f"Touched date on file {touched}")
|
||||||
|
|
||||||
return ExportResults(
|
return ExportResults(
|
||||||
results_exported,
|
results_exported,
|
||||||
@@ -2284,7 +2301,7 @@ def export_photo(
|
|||||||
results_updated,
|
results_updated,
|
||||||
results_skipped,
|
results_skipped,
|
||||||
results_exif_updated,
|
results_exif_updated,
|
||||||
results_touched
|
results_touched,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,12 @@ class ExportDB(ExportDB_ABC):
|
|||||||
(filename,),
|
(filename,),
|
||||||
)
|
)
|
||||||
results = c.fetchone()
|
results = c.fetchone()
|
||||||
stats = results[0:3] if results else None
|
if results:
|
||||||
|
stats = results[0:3]
|
||||||
|
mtime = int(stats[2]) if stats[2] is not None else None
|
||||||
|
stats = (stats[0], stats[1], mtime)
|
||||||
|
else:
|
||||||
|
stats = (None, None, None)
|
||||||
except Error as e:
|
except Error as e:
|
||||||
logging.warning(e)
|
logging.warning(e)
|
||||||
stats = (None, None, None)
|
stats = (None, None, None)
|
||||||
@@ -232,7 +237,12 @@ class ExportDB(ExportDB_ABC):
|
|||||||
(filename,),
|
(filename,),
|
||||||
)
|
)
|
||||||
results = c.fetchone()
|
results = c.fetchone()
|
||||||
stats = results[0:3] if results else None
|
if results:
|
||||||
|
stats = results[0:3]
|
||||||
|
mtime = int(stats[2]) if stats[2] is not None else None
|
||||||
|
stats = (stats[0], stats[1], mtime)
|
||||||
|
else:
|
||||||
|
stats = (None, None, None)
|
||||||
except Error as e:
|
except Error as e:
|
||||||
logging.warning(e)
|
logging.warning(e)
|
||||||
stats = (None, None, None)
|
stats = (None, None, None)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.33.1"
|
__version__ = "0.33.2"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class FileUtilABC(ABC):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def cmp(cls, file1, file2, mtime1 = None):
|
def cmp(cls, file1, file2, mtime1=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -119,7 +119,7 @@ class FileUtilMacOS(FileUtilABC):
|
|||||||
os.utime(path, times)
|
os.utime(path, times)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cmp(cls, f1, f2, mtime1 = None):
|
def cmp(cls, f1, f2, mtime1=None):
|
||||||
"""Does shallow compare (file signatures) of f1 to file f2.
|
"""Does shallow compare (file signatures) of f1 to file f2.
|
||||||
Arguments:
|
Arguments:
|
||||||
f1 -- File name
|
f1 -- File name
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ def export2(
|
|||||||
logging.debug(f"Skipping missing RAW photo for {filename}")
|
logging.debug(f"Skipping missing RAW photo for {filename}")
|
||||||
else:
|
else:
|
||||||
# use_photo_export
|
# use_photo_export
|
||||||
exported = None
|
exported = []
|
||||||
# export live_photo .mov file?
|
# export live_photo .mov file?
|
||||||
live_photo = True if live_photo and self.live_photo else False
|
live_photo = True if live_photo and self.live_photo else False
|
||||||
if edited:
|
if edited:
|
||||||
@@ -639,7 +639,7 @@ def export2(
|
|||||||
filestem = dest.stem
|
filestem = dest.stem
|
||||||
else:
|
else:
|
||||||
# didn't get passed a filename, add _edited
|
# didn't get passed a filename, add _edited
|
||||||
filestem = f"{dest.stem}_edited"
|
filestem = f"{dest.stem}{edited_identifier}"
|
||||||
dest = dest.parent / f"{filestem}.jpeg"
|
dest = dest.parent / f"{filestem}.jpeg"
|
||||||
|
|
||||||
exported = _export_photo_uuid_applescript(
|
exported = _export_photo_uuid_applescript(
|
||||||
@@ -668,9 +668,16 @@ def export2(
|
|||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
if exported is not None:
|
if exported:
|
||||||
# TODO: add touch_file code
|
if touch_file:
|
||||||
|
for exported_file in exported:
|
||||||
|
touched_files.append(exported_file)
|
||||||
|
ts = int(self.date.timestamp())
|
||||||
|
fileutil.utime(exported_file, (ts, ts))
|
||||||
exported_files.extend(exported)
|
exported_files.extend(exported)
|
||||||
|
if update:
|
||||||
|
update_new_files.extend(exported)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
f"Error exporting photo {self.uuid} to {dest} with use_photos_export"
|
f"Error exporting photo {self.uuid} to {dest} with use_photos_export"
|
||||||
@@ -712,7 +719,6 @@ def export2(
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
# if exiftool, write the metadata
|
# if exiftool, write the metadata
|
||||||
# TODO: add touch_file code to update touch time
|
|
||||||
if update:
|
if update:
|
||||||
exif_files = update_new_files + update_updated_files + update_skipped_files
|
exif_files = update_new_files + update_updated_files + update_skipped_files
|
||||||
else:
|
else:
|
||||||
@@ -773,6 +779,7 @@ def export2(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
)
|
)
|
||||||
|
|
||||||
export_db.set_exifdata_for_file(
|
export_db.set_exifdata_for_file(
|
||||||
exported_file,
|
exported_file,
|
||||||
self._exiftool_json_sidecar(
|
self._exiftool_json_sidecar(
|
||||||
@@ -787,7 +794,15 @@ def export2(
|
|||||||
)
|
)
|
||||||
exif_files_updated.append(exported_file)
|
exif_files_updated.append(exported_file)
|
||||||
|
|
||||||
return ExportResults(
|
if touch_file:
|
||||||
|
for exif_file in exif_files_updated:
|
||||||
|
touched_files.append(exported_file)
|
||||||
|
ts = int(self.date.timestamp())
|
||||||
|
fileutil.utime(exported_file, (ts, ts))
|
||||||
|
|
||||||
|
touched_files = list(set(touched_files))
|
||||||
|
|
||||||
|
results = ExportResults(
|
||||||
exported_files,
|
exported_files,
|
||||||
update_new_files,
|
update_new_files,
|
||||||
update_updated_files,
|
update_updated_files,
|
||||||
@@ -795,6 +810,7 @@ def export2(
|
|||||||
exif_files_updated,
|
exif_files_updated,
|
||||||
touched_files,
|
touched_files,
|
||||||
)
|
)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def _export_photo(
|
def _export_photo(
|
||||||
@@ -863,22 +879,21 @@ def _export_photo(
|
|||||||
touched_files.append(dest_str)
|
touched_files.append(dest_str)
|
||||||
else:
|
else:
|
||||||
# update, destination exists, but we might not need to replace it...
|
# update, destination exists, but we might not need to replace it...
|
||||||
cmp_orig = fileutil.cmp(src, dest)
|
if exiftool:
|
||||||
cmp_touch = fileutil.cmp(src, dest, mtime1=self.date.timestamp())
|
sig_exif = export_db.get_stat_exif_for_file(dest_str)
|
||||||
|
cmp_orig = fileutil.cmp_file_sig(dest_str, sig_exif)
|
||||||
|
sig_exif = (sig_exif[0], sig_exif[1], int(self.date.timestamp()))
|
||||||
|
cmp_touch = fileutil.cmp_file_sig(dest_str, sig_exif)
|
||||||
|
else:
|
||||||
|
cmp_orig = fileutil.cmp(src, dest)
|
||||||
|
cmp_touch = fileutil.cmp(src, dest, mtime1=self.date.timestamp())
|
||||||
|
|
||||||
sig_cmp = cmp_touch if touch_file else cmp_orig
|
sig_cmp = cmp_touch if touch_file else cmp_orig
|
||||||
|
|
||||||
if (export_as_hardlink and dest.samefile(src)) or (
|
if (export_as_hardlink and dest.samefile(src)) or (
|
||||||
not export_as_hardlink
|
not export_as_hardlink and not dest.samefile(src) and sig_cmp
|
||||||
and not dest.samefile(src)
|
|
||||||
and (
|
|
||||||
(
|
|
||||||
exiftool
|
|
||||||
and fileutil.cmp_file_sig(
|
|
||||||
dest_str, export_db.get_stat_exif_for_file(dest_str)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
or (not exiftool and sig_cmp)
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
|
# destination exists and signatures match, skip it
|
||||||
update_skipped_files.append(dest_str)
|
update_skipped_files.append(dest_str)
|
||||||
else:
|
else:
|
||||||
# destination exists but signature is different
|
# destination exists but signature is different
|
||||||
@@ -910,18 +925,18 @@ def _export_photo(
|
|||||||
else:
|
else:
|
||||||
fileutil.copy(src, dest_str, norsrc=no_xattr)
|
fileutil.copy(src, dest_str, norsrc=no_xattr)
|
||||||
|
|
||||||
if touched_files:
|
export_db.set_data(
|
||||||
ts = self.date.timestamp()
|
dest_str,
|
||||||
fileutil.utime(dest, (ts, ts))
|
self.uuid,
|
||||||
|
fileutil.file_sig(dest_str),
|
||||||
|
(None, None, None),
|
||||||
|
self.json(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
export_db.set_data(
|
if touched_files:
|
||||||
dest_str,
|
ts = int(self.date.timestamp())
|
||||||
self.uuid,
|
fileutil.utime(dest, (ts, ts))
|
||||||
fileutil.file_sig(dest_str),
|
|
||||||
(None, None, None),
|
|
||||||
self.json(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
return ExportResults(
|
return ExportResults(
|
||||||
exported_files + update_new_files + update_updated_files,
|
exported_files + update_new_files + update_updated_files,
|
||||||
|
|||||||
@@ -2304,6 +2304,16 @@ def test_export_update_exiftool():
|
|||||||
in result.output
|
in result.output
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# update with exiftool again, should be no changes
|
||||||
|
result = runner.invoke(
|
||||||
|
export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "--update", "--exiftool"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 8 photos, updated EXIF data: 0 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_export_update_hardlink():
|
def test_export_update_hardlink():
|
||||||
""" test export with hardlink then update """
|
""" test export with hardlink then update """
|
||||||
@@ -2747,6 +2757,197 @@ def test_export_touch_files_update():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
||||||
|
def test_export_touch_files_exiftool_update():
|
||||||
|
""" test complex export scenario with --update, --exiftol, and --touch-files """
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import time
|
||||||
|
|
||||||
|
import osxphotos
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
# basic export with dry-run
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[os.path.join(cwd, PHOTOS_DB_15_6), ".", "--export-by-date", "--dry-run"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert "Exported: 16 photos" in result.output
|
||||||
|
|
||||||
|
assert not pathlib.Path(CLI_EXPORT_BY_DATE[0]).is_file()
|
||||||
|
|
||||||
|
# without dry-run
|
||||||
|
result = runner.invoke(
|
||||||
|
export, [os.path.join(cwd, PHOTOS_DB_15_6), ".", "--export-by-date"]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert "Exported: 16 photos" in result.output
|
||||||
|
|
||||||
|
assert pathlib.Path(CLI_EXPORT_BY_DATE[0]).is_file()
|
||||||
|
|
||||||
|
# --update
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[os.path.join(cwd, PHOTOS_DB_15_6), ".", "--export-by-date", "--update"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 16 photos, updated EXIF data: 0 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
# --update --exiftool --dry-run
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
"--dry-run",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 16 photos, skipped: 0 photos, updated EXIF data: 16 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
# --update --exiftool
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 16 photos, skipped: 0 photos, updated EXIF data: 16 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
# --update --touch-file --exiftool --dry-run
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
"--touch-file",
|
||||||
|
"--dry-run",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 16 photos, updated EXIF data: 0 photos, touched date: 16 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
for fname, mtime in zip(CLI_EXPORT_BY_DATE, CLI_EXPORT_BY_DATE_TOUCH_TIMES):
|
||||||
|
st = os.stat(fname)
|
||||||
|
assert int(st.st_mtime) != mtime
|
||||||
|
|
||||||
|
# --update --touch-file --exiftool
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
"--touch-file",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 16 photos, updated EXIF data: 0 photos, touched date: 16 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
for fname, mtime in zip(CLI_EXPORT_BY_DATE, CLI_EXPORT_BY_DATE_TOUCH_TIMES):
|
||||||
|
st = os.stat(fname)
|
||||||
|
assert int(st.st_mtime) == mtime
|
||||||
|
|
||||||
|
# touch one file and run update again
|
||||||
|
ts = time.time()
|
||||||
|
os.utime(CLI_EXPORT_BY_DATE[0], (ts, ts))
|
||||||
|
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
"--touch-file",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 1 photo, skipped: 15 photos, updated EXIF data: 1 photo, touched date: 1 photo"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
for fname, mtime in zip(CLI_EXPORT_BY_DATE, CLI_EXPORT_BY_DATE_TOUCH_TIMES):
|
||||||
|
st = os.stat(fname)
|
||||||
|
assert int(st.st_mtime) == mtime
|
||||||
|
|
||||||
|
# run --update --exiftool --touch-file again
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--update",
|
||||||
|
"--exiftool",
|
||||||
|
"--touch-file",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 16 photos, updated EXIF data: 0 photos, touched date: 0 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
# run update without --touch-file
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||||
|
".",
|
||||||
|
"--export-by-date",
|
||||||
|
"--exiftool",
|
||||||
|
"--update",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Exported: 0 photos, updated: 0 photos, skipped: 16 photos, updated EXIF data: 0 photos"
|
||||||
|
in result.output
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_labels():
|
def test_labels():
|
||||||
"""Test osxphotos labels """
|
"""Test osxphotos labels """
|
||||||
import json
|
import json
|
||||||
|
|||||||
Reference in New Issue
Block a user