diff --git a/osxphotos/cli.py b/osxphotos/cli.py index dd8f25c3..e17db5aa 100644 --- a/osxphotos/cli.py +++ b/osxphotos/cli.py @@ -20,7 +20,7 @@ import osxmetadata import photoscript import rich.traceback import yaml -from rich import pretty +from rich import pretty, print import osxphotos @@ -60,9 +60,11 @@ from .photoexporter import ExportResults, PhotoExporter from .photoinfo import PhotoInfo from .photokit import check_photokit_authorization, request_photokit_authorization from .photosalbum import PhotosAlbum +from .photosdb.photosdb_utils import get_photos_library_version from .phototemplate import PhotoTemplate, RenderOptions from .pyrepl import embed_repl from .queryoptions import QueryOptions +from .sqlgrep import sqlgrep from .uti import get_preferred_uti_extension from .utils import expand_and_validate_filepath, load_function, normalize_fs_path @@ -4283,3 +4285,49 @@ def repl(ctx, cli_obj, db, emacs): quit_words=["q", "quit", "exit"], vi_mode=not emacs, ) + + +@cli.command(hidden=True) +@DB_OPTION +@click.pass_obj +@click.pass_context +@click.option( + "--ignore-case", + "-i", + required=False, + is_flag=True, + default=False, + help="Ignore case when searching (default is case-sensitive)", +) +@click.option( + "--print-filename", + "-p", + required=False, + is_flag=True, + default=False, + help="Print name of database file when printing results", +) +@click.argument("pattern", metavar="PATTERN", required=True) +def grep(ctx, cli_obj, db, ignore_case, print_filename, pattern): + """Search for PATTERN in the Photos sqlite database file""" + db = db or get_photos_db() + db = pathlib.Path(db) + if db.is_file(): + # if passed the actual database, really want the parent of the database directory + db = db.parent.parent + photos_ver = get_photos_library_version(str(db)) + if photos_ver < 5: + db_file = db / "database" / "photos.db" + else: + db_file = db / "database" / "Photos.sqlite" + + if not db_file.is_file(): + click.secho(f"Could not find database file {db_file}", fg="red") + ctx.exit(2) + + db_file = str(db_file) + + for table, column, row_id, value in sqlgrep( + db_file, pattern, ignore_case, print_filename, rich_markup=True + ): + print(", ".join([table, column, row_id, value])) diff --git a/osxphotos/photosdb/photosdb_utils.py b/osxphotos/photosdb/photosdb_utils.py index aa83fa3e..4d0d49da 100644 --- a/osxphotos/photosdb/photosdb_utils.py +++ b/osxphotos/photosdb/photosdb_utils.py @@ -1,6 +1,7 @@ """ utility functions used by PhotosDB """ import logging +import pathlib import plistlib from .._constants import ( @@ -17,7 +18,7 @@ from ..utils import _open_sql_file def get_db_version(db_file): - """ Gets the Photos DB version from LiGlobals table + """Gets the Photos DB version from LiGlobals table Args: db_file: path to photos.db database file containing LiGlobals table @@ -44,11 +45,11 @@ def get_db_version(db_file): def get_model_version(db_file): - """ Returns the database model version from Z_METADATA - + """Returns the database model version from Z_METADATA + Args: db_file: path to Photos.sqlite database file containing Z_METADATA table - + Returns: model version as str """ @@ -67,11 +68,11 @@ def get_model_version(db_file): def get_db_model_version(db_file): - """ Returns Photos version based on model version found in db_file - + """Returns Photos version based on model version found in db_file + Args: db_file: path to Photos.sqlite file - + Returns: int of major Photos version number (e.g. 5 or 6). If unknown model version found, logs warning and returns most current Photos version. """ @@ -94,7 +95,7 @@ class UnknownLibraryVersion(Exception): def get_photos_library_version(library_path): - """Return int indicating which Photos version a library was created with """ + """Return int indicating which Photos version a library was created with""" library_path = pathlib.Path(library_path) db_ver = get_db_version(str(library_path / "database" / "photos.db")) db_ver = int(db_ver)