Feature add import 754 (#762)

* Initial alpha version of import command

* Refactored

* Improved help, added --clear-metadata

* Added --clear-metadata, --exiftool to import

* Added --keyword, --title, --description

* Added --location

* Added test for --location

* Changed --auto-folder to --split-folder, added docs

* Added --walk, updated docs

* Added --check-templates

* Updated help text for import
This commit is contained in:
Rhet Turnbull
2022-08-21 09:07:22 -07:00
committed by GitHub
parent 46738d05b2
commit c88fc75013
11 changed files with 1846 additions and 23 deletions

View File

@@ -15,6 +15,7 @@ from .export import export
from .exportdb import exportdb
from .grep import grep
from .help import help
from .import_cli import import_cli
from .info import info
from .install_uninstall_run import install, run, uninstall
from .keywords import keywords
@@ -75,6 +76,7 @@ for command in [
exportdb,
grep,
help,
import_cli,
info,
install,
keywords,

1072
osxphotos/cli/import_cli.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ from typing import List, Optional
import photoscript
from more_itertools import chunked
from photoscript import Photo, PhotosLibrary
from photoscript import Album, Folder, Photo, PhotosLibrary
from .photoinfo import PhotoInfo
from .utils import noop, pluralize
@@ -51,19 +51,72 @@ class PhotosAlbum:
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()
verbose = verbose or noop
top_folder_name = folders.pop(0)
top_folder = library.folder(top_folder_name, top_level=True)
if not top_folder:
verbose(f"Creating folder '{top_folder_name}'")
top_folder = library.create_folder(top_folder_name)
current_folder = top_folder
for folder_name in folders:
folder = current_folder.folder(folder_name)
if not folder:
verbose(f"Creating folder '{folder_name}'")
folder = current_folder.create_folder(folder_name)
current_folder = folder
return current_folder
def album_by_path(
folders_album: List[str], verbose: Optional[callable] = None
) -> Album:
"""Get (and create if necessary) a Photos Album by path (pass as list of folders, album name)"""
library = PhotosLibrary()
verbose = verbose or noop
if len(folders_album) > 1:
# have folders
album_name = folders_album.pop()
folder = folder_by_path(folders_album, verbose)
album = folder.album(album_name)
if not album:
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:
verbose(f"Creating album '{album_name}'")
album = library.create_album(album_name)
return album
class PhotosAlbumPhotoScript:
"""Add photoscript.Photo objects to album"""
def __init__(self, name: str, verbose: Optional[callable] = None):
self.name = name
def __init__(
self, name: str, verbose: Optional[callable] = None, split_folder: Optional[str] = None
):
"""Return a PhotosAlbumPhotoScript 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='/',
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
"""
self.verbose = verbose or noop
self.library = 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
folders_album = name.split(split_folder) if split_folder else [name]
self.album = album_by_path(folders_album, verbose=verbose)
self.name = name
def add(self, photo: Photo):
self.album.add([photo])

View File

@@ -1629,20 +1629,18 @@ def _get_pathlib_value(field, value, quote):
if len(parts) == 1:
return shlex.quote(value) if quote else value
if len(parts) > 2:
raise ValueError(f"Illegal value for path template: {field}")
path = parts[0]
attribute = parts[1]
path = pathlib.Path(value)
try:
val = getattr(path, attribute)
val_str = str(val)
if quote:
val_str = shlex.quote(val_str)
return val_str
except AttributeError:
raise ValueError("Illegal value for path template: {attribute}")
for attribute in parts[1:]:
try:
val = getattr(path, attribute)
path = pathlib.Path(val)
except AttributeError as e:
raise ValueError(f"Illegal value for filepath template: {attribute}") from e
val_str = str(val)
if quote:
val_str = shlex.quote(val_str)
return val_str
def format_str_value(value, format_str):