diff --git a/osxphotos/cli/import_cli.py b/osxphotos/cli/import_cli.py index c13e3876..c73edef1 100644 --- a/osxphotos/cli/import_cli.py +++ b/osxphotos/cli/import_cli.py @@ -214,7 +214,7 @@ def add_photo_to_albums( for a in albums: verbose(f"Adding photo [filename]{filepath.name}[/] to album [filepath]{a}[/]") photos_album = PhotosAlbumPhotoScript( - a, verbose=verbose, split_folder=split_folder + a, verbose=verbose, split_folder=split_folder, rich=True ) photos_album.add(photo) diff --git a/osxphotos/photosalbum.py b/osxphotos/photosalbum.py index 8f568d4d..c2218238 100644 --- a/osxphotos/photosalbum.py +++ b/osxphotos/photosalbum.py @@ -12,45 +12,6 @@ from .utils import noop, pluralize __all__ = ["PhotosAlbum", "PhotosAlbumPhotoScript"] -class PhotosAlbum: - """Add osxphotos.photoinfo.PhotoInfo objects to album""" - - def __init__(self, name: str, verbose: Optional[callable] = None): - self.name = name - self.verbose = verbose or noop - self.library = photoscript.PhotosLibrary() - - album = self.library.album(name) - if album is None: - self.verbose(f"Creating Photos album '{self.name}'") - album = self.library.create_album(name) - self.album = album - - def add(self, photo: PhotoInfo): - photo_ = photoscript.Photo(photo.uuid) - self.album.add([photo_]) - self.verbose( - f"Added {photo.original_filename} ({photo.uuid}) to album {self.name}" - ) - - def add_list(self, photo_list: List[PhotoInfo]): - photos = [] - for p in photo_list: - try: - photos.append(photoscript.Photo(p.uuid)) - except Exception as e: - self.verbose(f"Error creating Photo object for photo {p.uuid}: {e}") - for photolist in chunked(photos, 10): - self.album.add(photolist) - photo_len = len(photo_list) - self.verbose( - f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}" - ) - - def photos(self): - return self.album.photos() - - def folder_by_path(folders: List[str], verbose: Optional[callable] = None) -> Folder: """Get (and create if necessary) a Photos Folder by path (passed as list of folder names)""" library = PhotosLibrary() @@ -81,54 +42,105 @@ def album_by_path( album_name = folders_album.pop() folder = folder_by_path(folders_album, verbose) album = folder.album(album_name) - if not album: + if album is None: verbose(f"Creating album '{album_name}'") album = folder.create_album(album_name) else: # only have album name album_name = folders_album[0] album = library.album(album_name, top_level=True) - if not album: + if album is None: verbose(f"Creating album '{album_name}'") album = library.create_album(album_name) return album -class PhotosAlbumPhotoScript: - """Add photoscript.Photo objects to album""" +class PhotosAlbum: + """Add osxphotos.photoinfo.PhotoInfo objects to album""" def __init__( - self, name: str, verbose: Optional[callable] = None, split_folder: Optional[str] = None + self, + name: str, + verbose: Optional[callable] = None, + split_folder: Optional[str] = None, + rich: bool = False, ): - """Return a PhotosAlbumPhotoScript object, creating the album if necessary + """Return a PhotosAlbum object, creating the album if necessary Args: name: Name of album verbose: optional callable to print verbose output split_folder: if set, split album name on value of split_folder to create folders if necessary, - e.g. if name = 'folder1/folder2/album' and split_folder='/', + e.g. if name = 'folder1/folder2/album' and split_folder='/', then folders 'folder1' and 'folder2' will be created and album 'album' will be created in 'folder2'; if not set, album 'folder1/folder2/album' will be created + rich: if True, use rich themes for verbose output """ self.verbose = verbose or noop - self.library = PhotosLibrary() + self.library = photoscript.PhotosLibrary() folders_album = name.split(split_folder) if split_folder else [name] self.album = album_by_path(folders_album, verbose=verbose) self.name = name + self.rich = rich + + def add(self, photo: PhotoInfo): + photo_ = photoscript.Photo(photo.uuid) + self.album.add([photo_]) + self.verbose( + f"Added {self._format_name(photo.original_filename)} ({self._format_uuid(photo.uuid)}) to album {self._format_album(self.name)}" + ) + + def add_list(self, photo_list: List[PhotoInfo]): + photos = [] + for p in photo_list: + try: + photos.append(photoscript.Photo(p.uuid)) + except Exception as e: + self.verbose( + f"Error creating Photo object for photo {self._format_uuid(p.uuid)}: {e}" + ) + for photolist in chunked(photos, 10): + self.album.add(photolist) + photo_len = len(photo_list) + self.verbose( + f"Added {self._format_num(photo_len)} {pluralize(photo_len, 'photo', 'photos')} to album {self._format_album(self.name)}" + ) + + def photos(self): + return self.album.photos() + + def _format_uuid(self, uuid: str) -> str: + """ "Format uuid for verbose output""" + return f"[uuid]{uuid}[/uuid]" if self.rich else uuid + + def _format_album(self, album: str) -> str: + """ "Format album name for verbose output""" + return f"[filepath]{album}[/filepath]" if self.rich else album + + def _format_name(self, name: str) -> str: + """ "Format name for verbose output""" + return f"[filename]{name}[/filename]" if self.rich else name + + def _format_num(self, num: int) -> str: + """ "Format number for verbose output""" + return f"[num]{num}[/num]" if self.rich else str(num) + + +class PhotosAlbumPhotoScript(PhotosAlbum): + """Add photoscript.Photo objects to album""" def add(self, photo: Photo): self.album.add([photo]) - self.verbose(f"Added {photo.filename} ({photo.uuid}) to album {self.name}") + self.verbose( + f"Added {self._format_name(photo.filename)} ({self._format_uuid(photo.uuid)}) to album {self._format_album(self.name)}" + ) def add_list(self, photo_list: List[Photo]): for photolist in chunked(photo_list, 10): self.album.add(photolist) photo_len = len(photo_list) self.verbose( - f"Added {photo_len} {pluralize(photo_len, 'photo', 'photos')} to album {self.name}" + f"Added {self._format_num(photo_len)} {pluralize(photo_len, 'photo', 'photos')} to album {self._format_album(self.name)}" ) - - def photos(self): - return self.album.photos() diff --git a/osxphotos/phototemplate.py b/osxphotos/phototemplate.py index 5d157d77..d386d271 100644 --- a/osxphotos/phototemplate.py +++ b/osxphotos/phototemplate.py @@ -1562,7 +1562,7 @@ class PhotoTemplate: # if no uuid, then template is being validated but not actually run # so don't run the function values = [] - elif caller == "export": + elif caller in ["export", "query"]: # function signature is: # def example(photo: PhotoInfo, options: ExportOptions, args: Optional[str] = None, **kwargs) -> Union[List, str]: values = template_func(self.photo, options=self.options, args=field_arg) diff --git a/tests/conftest.py b/tests/conftest.py index f0f3cfd8..82ab31c2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -206,7 +206,7 @@ def copy_photos_library(photos_library, delay=0): @pytest.fixture def addalbum_library(): - copy_photos_library(delay=10) + copy_photos_library(TEST_LIBRARY, delay=10) def copy_photos_library_to_path(photos_library_path: str, dest_path: str) -> str: diff --git a/tests/test_cli_add_to_album.py b/tests/test_cli_add_to_album.py index 56c47397..394a7f9c 100644 --- a/tests/test_cli_add_to_album.py +++ b/tests/test_cli_add_to_album.py @@ -38,8 +38,8 @@ def test_export_add_to_album(addalbum_library): ], ) assert result.exit_code == 0 - assert f"Creating Photos album '{EXPORT_ALBUM}'" in result.output - assert f"Creating Photos album '{SKIP_ALBUM}'" in result.output + assert f"Creating album '{EXPORT_ALBUM}'" in result.output + assert f"Creating album '{SKIP_ALBUM}'" in result.output photoslib = photoscript.PhotosLibrary() album = photoslib.album(EXPORT_ALBUM) @@ -69,9 +69,9 @@ def test_export_add_to_album(addalbum_library): ], ) assert result.exit_code == 0 - assert f"Creating Photos album '{EXPORT_ALBUM}'" not in result.output - assert f"Creating Photos album '{SKIP_ALBUM}'" not in result.output - assert f"Creating Photos album '{MISSING_ALBUM}'" in result.output + assert f"Creating album '{EXPORT_ALBUM}'" not in result.output + assert f"Creating album '{SKIP_ALBUM}'" not in result.output + assert f"Creating album '{MISSING_ALBUM}'" in result.output photoslib = photoscript.PhotosLibrary() export_album = photoslib.album(EXPORT_ALBUM)