Compare commits

...

4 Commits

Author SHA1 Message Date
Rhet Turnbull
b4897ff1b5 version bump [skip ci] 2022-01-06 22:16:12 -08:00
Rhet Turnbull
661a573bf5 Fix for #570 2022-01-06 22:13:25 -08:00
Rhet Turnbull
0c9bd87602 More refactoring of export code, #462 2022-01-06 05:40:47 -08:00
Rhet Turnbull
896d888710 Updated CHANGELOG.md [skip ci] 2022-01-04 06:35:23 -08:00
7 changed files with 43 additions and 67 deletions

View File

@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v0.44.4](https://github.com/RhetTbull/osxphotos/compare/v0.44.3...v0.44.4)
> 4 January 2022
- Refactored photoinfo, photoexporter; #462 [`a73dc72`](https://github.com/RhetTbull/osxphotos/commit/a73dc72558b77152f4c90f143b6a60924b8905c8)
- More refactoring of export code, #462 [`147b30f`](https://github.com/RhetTbull/osxphotos/commit/147b30f97308db65868dc7a8d177d77ad0d0ad40)
- Export DB can now reside outside export directory, #568 [`76aee7f`](https://github.com/RhetTbull/osxphotos/commit/76aee7f189b4b32e2e263a4e798711713ed17a14)
#### [v0.44.3](https://github.com/RhetTbull/osxphotos/compare/v0.44.2...v0.44.3) #### [v0.44.3](https://github.com/RhetTbull/osxphotos/compare/v0.44.2...v0.44.3)
> 31 December 2021 > 31 December 2021

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.44.4" __version__ = "0.44.5"

View File

@@ -2954,11 +2954,9 @@ def export_photo_to_directory(
try: try:
exporter = PhotoExporter(photo) exporter = PhotoExporter(photo)
export_results = exporter.export2( export_results = exporter.export2(
dest_path, dest=dest_path,
original_filename=filename,
edited=edited, edited=edited,
original=export_original, filename=filename,
edited_filename=filename,
sidecar=sidecar_flags, sidecar=sidecar_flags,
sidecar_drop_ext=sidecar_drop_ext, sidecar_drop_ext=sidecar_drop_ext,
live_photo=export_live, live_photo=export_live,

View File

@@ -292,10 +292,8 @@ class PhotoExporter:
results = self.export2( results = self.export2(
dest, dest,
original=not edited, filename=filename,
original_filename=filename,
edited=edited, edited=edited,
edited_filename=filename,
live_photo=live_photo, live_photo=live_photo,
raw_photo=raw_photo, raw_photo=raw_photo,
export_as_hardlink=export_as_hardlink, export_as_hardlink=export_as_hardlink,
@@ -317,10 +315,8 @@ class PhotoExporter:
def export2( def export2(
self, self,
dest, dest,
original=True, filename=None,
original_filename=None,
edited=False, edited=False,
edited_filename=None,
live_photo=False, live_photo=False,
raw_photo=False, raw_photo=False,
export_as_hardlink=False, export_as_hardlink=False,
@@ -368,8 +364,7 @@ class PhotoExporter:
in which case export will use the extension provided by Photos upon export. in which case export will use the extension provided by Photos upon export.
e.g. to get the extension of the edited photo, e.g. to get the extension of the edited photo,
reference PhotoInfo.path_edited reference PhotoInfo.path_edited
original: (boolean, default=True); if True, will export the original version of the photo edited: (boolean, default=False); if True will export the edited version of the photo otherwise exports the original version
edited: (boolean, default=False); if True will export the edited version of the photo (only one of original or edited can be used)
live_photo: (boolean, default=False); if True, will also export the associated .mov for live photos live_photo: (boolean, default=False); if True, will also export the associated .mov for live photos
raw_photo: (boolean, default=False); if True, will also export the associated RAW photo raw_photo: (boolean, default=False); if True, will also export the associated RAW photo
export_as_hardlink: (boolean, default=False); if True, will hardlink files instead of copying them export_as_hardlink: (boolean, default=False); if True, will hardlink files instead of copying them
@@ -452,16 +447,13 @@ class PhotoExporter:
if verbose and not callable(verbose): if verbose and not callable(verbose):
raise TypeError("verbose must be callable") raise TypeError("verbose must be callable")
if verbose is None: if verbose is None:
verbose = self._verbose verbose = self._verbose
self._render_options = render_options or RenderOptions() self._render_options = render_options or RenderOptions()
export_original = original export_original = not edited
export_edited = edited export_edited = edited
if export_original and export_edited:
raise ValueError("Cannot export both original and edited photos")
if export_edited and not self.photo.hasadjustments: if export_edited and not self.photo.hasadjustments:
raise ValueError( raise ValueError(
"Photo does not have adjustments, cannot export edited version" "Photo does not have adjustments, cannot export edited version"
@@ -473,13 +465,13 @@ class PhotoExporter:
elif not dry_run and not os.path.isdir(dest): elif not dry_run and not os.path.isdir(dest):
raise FileNotFoundError("Invalid path passed to export") raise FileNotFoundError("Invalid path passed to export")
original_filename = original_filename or self.photo.original_filename if export_edited:
dest_original = pathlib.Path(dest) / original_filename filename = filename or self._get_edited_filename(
self.photo.original_filename
edited_filename = edited_filename or self._get_edited_filename( )
original_filename else:
) filename = filename or self.photo.original_filename
dest_edited = pathlib.Path(dest) / edited_filename dest = pathlib.Path(dest) / filename
# Is there something to convert? # Is there something to convert?
if convert_to_jpeg and self.photo.isphoto: if convert_to_jpeg and self.photo.isphoto:
@@ -488,39 +480,25 @@ class PhotoExporter:
if export_original and self.photo.uti_original != "public.jpeg": if export_original and self.photo.uti_original != "public.jpeg":
# not a jpeg but will convert to jpeg upon export so fix file extension # not a jpeg but will convert to jpeg upon export so fix file extension
something_to_convert = True something_to_convert = True
dest_original = dest_original.parent / f"{dest_original.stem}{ext}" dest = dest.parent / f"{dest.stem}{ext}"
if export_edited and self.photo.uti != "public.jpeg": if export_edited and self.photo.uti != "public.jpeg":
# in Big Sur+, edited HEICs are HEIC # in Big Sur+, edited HEICs are HEIC
something_to_convert = True something_to_convert = True
dest_edited = dest_edited.parent / f"{dest_edited.stem}{ext}" dest_edited = dest.parent / f"{dest.stem}{ext}"
convert_to_jpeg = something_to_convert convert_to_jpeg = something_to_convert
else: else:
convert_to_jpeg = False convert_to_jpeg = False
# TODO: need to look at this to see what happens if original not being exported but edited exists and already has an increment dest, _ = self._validate_dest_path(
dest_original, increment_file_count = self._validate_dest_path( dest, increment=increment, update=update, overwrite=overwrite
dest_original, increment=increment, update=update, overwrite=overwrite
)
dest_original = pathlib.Path(dest_original)
if export_edited:
dest_edited, increment_file_count = self._validate_dest_path(
dest_edited,
increment=increment,
update=update,
overwrite=overwrite,
count=increment_file_count,
)
dest_edited = pathlib.Path(dest_edited)
self._render_options.filepath = (
str(dest_original) if export_original else str(dest_edited)
) )
dest = pathlib.Path(dest)
self._render_options.filepath = str(dest)
all_results = ExportResults() all_results = ExportResults()
if use_photos_export: if use_photos_export:
self._export_photo_with_photos_export( self._export_photo_with_photos_export(
dest=dest_original if export_original else dest_edited, dest=dest,
all_results=all_results, all_results=all_results,
fileutil=fileutil, fileutil=fileutil,
export_db=export_db, export_db=export_db,
@@ -540,17 +518,11 @@ class PhotoExporter:
# find the source file on disk and export # find the source file on disk and export
# get path to source file and verify it's not None and is valid file # get path to source file and verify it's not None and is valid file
# TODO: how to handle ismissing or not hasadjustments and edited=True cases? # TODO: how to handle ismissing or not hasadjustments and edited=True cases?
export_src_dest = [] src = self.photo.path_edited if edited else self.photo.path
if edited and self.photo.path_edited is not None: if src and not pathlib.Path(src).is_file():
export_src_dest.append((self.photo.path_edited, dest_edited)) raise FileNotFoundError(f"{src} does not appear to exist")
elif not edited and self.photo.path is not None:
export_src_dest.append((self.photo.path, dest_original))
# TODO: this for loop not necessary
for src, dest in export_src_dest:
if not pathlib.Path(src).is_file():
raise FileNotFoundError(f"{src} does not appear to exist")
if src:
# found source now try to find right destination # found source now try to find right destination
if update and dest.exists(): if update and dest.exists():
# destination exists, check to see if destination is the right UUID # destination exists, check to see if destination is the right UUID
@@ -595,11 +567,6 @@ class PhotoExporter:
# increment the destination file # increment the destination file
dest = pathlib.Path(increment_filename(dest)) dest = pathlib.Path(increment_filename(dest))
if export_original:
dest_original = dest
else:
dest_edited = dest
# export the dest file # export the dest file
results = self._export_photo( results = self._export_photo(
src, src,
@@ -618,8 +585,6 @@ class PhotoExporter:
) )
all_results += results all_results += results
dest = dest_original if export_original else dest_edited
# copy live photo associated .mov if requested # copy live photo associated .mov if requested
if ( if (
export_original export_original
@@ -730,7 +695,6 @@ class PhotoExporter:
sidecar_xmp_files_skipped = [] sidecar_xmp_files_skipped = []
sidecar_xmp_files_written = [] sidecar_xmp_files_written = []
dest = dest_original if export_original else dest_edited
dest_suffix = "" if sidecar_drop_ext else dest.suffix dest_suffix = "" if sidecar_drop_ext else dest.suffix
if sidecar & SIDECAR_JSON: if sidecar & SIDECAR_JSON:
sidecar_filename = dest.parent / pathlib.Path( sidecar_filename = dest.parent / pathlib.Path(

View File

@@ -725,8 +725,10 @@ class PhotoInfo:
self._uti_original = self.uti self._uti_original = self.uti
elif self._db._photos_ver >= 7: elif self._db._photos_ver >= 7:
# Monterey+ # Monterey+
self._uti_original = get_uti_for_extension( # there are some cases with UTI_original is None (photo imported with no extension) so fallback to UTI and hope it's right
pathlib.Path(self.original_filename).suffix self._uti_original = (
get_uti_for_extension(pathlib.Path(self.original_filename).suffix)
or self.uti
) )
else: else:
self._uti_original = self._info["UTI_original"] self._uti_original = self._info["UTI_original"]
@@ -1025,7 +1027,7 @@ class PhotoInfo:
@property @property
def israw(self): def israw(self):
"""returns True if photo is a raw image. For images with an associated RAW+JPEG pair, see has_raw""" """returns True if photo is a raw image. For images with an associated RAW+JPEG pair, see has_raw"""
return "raw-image" in self.uti_original return "raw-image" in self.uti_original if self.uti_original else False
@property @property
def raw_original(self): def raw_original(self):

View File

@@ -520,7 +520,8 @@ class PhotoAsset:
== Photos.PHAssetResourceTypeAlternatePhoto == Photos.PHAssetResourceTypeAlternatePhoto
): ):
data = self._request_resource_data(resource) data = self._request_resource_data(resource)
ext = pathlib.Path(self.raw_filename).suffix[1:] suffix = pathlib.Path(self.raw_filename).suffix
ext = suffix[1:] if suffix else ""
break break
else: else:
raise PhotoKitExportError( raise PhotoKitExportError(

View File

@@ -591,6 +591,9 @@ def get_preferred_uti_extension(uti):
def get_uti_for_extension(extension): def get_uti_for_extension(extension):
"""get UTI for a given file extension""" """get UTI for a given file extension"""
if not extension:
return None
# accepts extension with or without leading 0 # accepts extension with or without leading 0
if extension[0] == ".": if extension[0] == ".":
extension = extension[1:] extension = extension[1:]