Added --no-extended-attributes option to CLI, closes #85

This commit is contained in:
Rhet Turnbull
2020-04-05 09:13:52 -07:00
parent 6073acc9d3
commit ddaa66d19e
7 changed files with 96 additions and 10 deletions

View File

@@ -912,6 +912,14 @@ def query(
help="Optional template for specifying name of output directory in the form '{name,DEFAULT}'. "
"See below for additional details on templating system.",
)
@click.option(
"--no-extended-attributes",
is_flag=True,
default=False,
help="Don't copy extended attributes when exporting. You only need this if exporting "
"to a filesystem that doesn't support Mac OS extended attributes. Only use this if you get "
"an error while exporting.",
)
@DB_ARGUMENT
@click.argument("dest", nargs=1, type=click.Path(exists=True))
@click.pass_obj
@@ -975,6 +983,7 @@ def export(
directory,
place,
no_place,
no_extended_attributes,
):
""" Export photos from the Photos database.
Export path DEST is required.
@@ -1117,6 +1126,7 @@ def export(
download_missing,
exiftool,
directory,
no_extended_attributes,
)
else:
for p in photos:
@@ -1133,6 +1143,7 @@ def export(
download_missing,
exiftool,
directory,
no_extended_attributes,
)
if export_paths:
click.echo(f"Exported {p.filename} to {export_paths}")
@@ -1491,6 +1502,7 @@ def export_photo(
download_missing,
exiftool,
directory,
no_extended_attributes,
):
""" Helper function for export that does the actual export
photo: PhotoInfo object
@@ -1505,6 +1517,7 @@ def export_photo(
download_missing: attempt download of missing iCloud photos
exiftool: use exiftool to write EXIF metadata directly to exported photo
directory: template used to determine output directory
no_extended_attributes: boolean; if True, exports photo without preserving extended attributes
returns list of path(s) of exported photo or None if photo was missing
"""
@@ -1586,6 +1599,7 @@ def export_photo(
overwrite=overwrite,
use_photos_export=use_photos_export,
exiftool=exiftool,
no_xattr=no_extended_attributes,
)[0]
photo_paths.append(photo_path)
@@ -1620,6 +1634,7 @@ def export_photo(
edited=True,
use_photos_export=use_photos_export,
exiftool=exiftool,
no_xattr=no_extended_attributes,
)
return photo_paths

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.25.0"
__version__ = "0.25.1"

View File

@@ -522,6 +522,7 @@ class PhotoInfo:
use_photos_export=False,
timeout=120,
exiftool=False,
no_xattr=False,
):
""" export photo
dest: must be valid destination path (or exception raised)
@@ -545,6 +546,7 @@ class PhotoInfo:
use_photos_export: (boolean, default=False); if True will attempt to export photo via applescript interaction with Photos
timeout: (int, default=120) timeout in seconds used with use_photos_export
exiftool: (boolean, default = False); if True, will use exiftool to write metadata to export file
no_xattr: (boolean, default = False); if True, exports file without preserving extended attributes
returns list of full paths to the exported files """
# list of all files exported during this call to export
@@ -667,7 +669,7 @@ class PhotoInfo:
)
# copy the file, _copy_file uses ditto to preserve Mac extended attributes
_copy_file(src, dest)
_copy_file(src, dest, norsrc=no_xattr)
exported_files.append(str(dest))
# copy live photo associated .mov if requested
@@ -679,7 +681,7 @@ class PhotoInfo:
logging.debug(
f"Exporting live photo video of {filename} as {live_name.name}"
)
_copy_file(src_live, str(live_name))
_copy_file(src_live, str(live_name), norsrc=no_xattr)
exported_files.append(str(live_name))
else:
logging.warning(f"Skipping missing live movie for {filename}")

View File

@@ -114,10 +114,14 @@ def _dd_to_dms(dd):
return int(deg_), int(min_), sec_
def _copy_file(src, dest):
def _copy_file(src, dest, norsrc=False):
""" Copies a file from src path to dest path
src: source path as string
dest: destination path as string
norsrc: (bool) if True, uses --norsrc flag with ditto so it will not copy
resource fork or extended attributes. May be useful on volumes that
don't work with extended attributes (likely only certain SMB mounts)
default is False
Uses ditto to perform copy; will silently overwrite dest if it exists
Raises exception if copy fails or either path is None """
@@ -125,19 +129,24 @@ def _copy_file(src, dest):
raise ValueError("src and dest must not be None", src, dest)
if not os.path.isfile(src):
raise ValueError("src file does not appear to exist", src)
raise FileNotFoundError("src file does not appear to exist", src)
if norsrc:
command = ["/usr/bin/ditto", "--norsrc", src, dest]
else:
command = ["/usr/bin/ditto", src, dest]
# if error on copy, subprocess will raise CalledProcessError
try:
subprocess.run(
["/usr/bin/ditto", src, dest], check=True, stderr=subprocess.PIPE
)
result = subprocess.run(command, check=True, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
logging.critical(
f"ditto returned error: {e.returncode} {e.stderr.decode(sys.getfilesystemencoding()).rstrip()}"
)
raise e
return result.returncode
def dd_to_dms_str(lat, lon):
""" convert latitude, longitude in degrees to degrees, minutes, seconds as string """