Added list option to cmd_line. Closes #14

This commit is contained in:
Rhet Turnbull
2019-12-08 09:14:48 -08:00
parent 7fef67f852
commit aaba5cabf3
4 changed files with 125 additions and 78 deletions

View File

@@ -37,19 +37,20 @@ After installing pipx:
Usage: osxphotos [OPTIONS] COMMAND [ARGS]...
Options:
--db <Photos database path> Specify database file
--json Print output in JSON format
--db <Photos database path> Specify database file.
--json Print output in JSON format.
-v, --version Show the version and exit.
-h, --help Show this message and exit.
Commands:
albums print out albums found in the Photos library
dump print list of all photos & associated info from the Photos...
help print help; for help on commands: help <command>
info print out descriptive info of the Photos library database
keywords print out keywords found in the Photos library
persons print out persons (faces) found in the Photos library
query query the Photos database using 1 or more search options; if...
albums Print out albums found in the Photos library.
dump Print list of all photos & associated info from the Photos...
help Print help; for help on commands: help <command>.
info Print out descriptive info of the Photos library database.
keywords Print out keywords found in the Photos library.
list Print list of Photos libraries found on the system.
persons Print out persons (faces) found in the Photos library.
query Query the Photos database using 1 or more search options; if...
```
To get help on a specific command, use `osxphotos help <command_name>`
@@ -59,27 +60,29 @@ Example: `osxphotos help query`
```
Usage: osxphotos help [OPTIONS]
query the Photos database using 1 or more search options; if more than
Query the Photos database using 1 or more search options; if more than
one option is provided, they are treated as "AND" (e.g. search for photos
matching all options)
matching all options).
Options:
--keyword TEXT search for keyword(s)
--person TEXT search for person(s)
--album TEXT search for album(s)
--uuid TEXT search for UUID(s)
--name TEXT search for TEXT in name of photo
--no-name search for photos with no name
--description TEXT search for TEXT in description of photo
--no-description search for photos with no description
-i, --ignore-case case insensitive search for name or description. Does
not apply to keyword, person, or album
--favorite search for photos marked favorite
--not-favorite search for photos not marked favorite
--hidden search for photos marked hidden
--not-hidden search for photos not marked hidden
--missing search for photos missing from disk
--not-missing search for photos present on disk (e.g. not missing)
--keyword TEXT Search for keyword(s).
--person TEXT Search for person(s).
--album TEXT Search for album(s).
--uuid TEXT Search for UUID(s).
--name TEXT Search for TEXT in name of photo.
--no-name Search for photos with no name.
--description TEXT Search for TEXT in description of photo.
--no-description Search for photos with no description.
-i, --ignore-case Case insensitive search for name or description. Does
not apply to keyword, person, or album.
--edited Search for photos that have been edited.
--external-edit Search for photos edited in external editor.
--favorite Search for photos marked favorite.
--not-favorite Search for photos not marked favorite.
--hidden Search for photos marked hidden.
--not-hidden Search for photos not marked hidden.
--missing Search for photos missing from disk.
--not-missing Search for photos present on disk (e.g. not missing).
--json Print output in JSON format
-h, --help Show this message and exit.
```
@@ -141,7 +144,7 @@ Returns path to last opened Photo Library as string.
#### ```list_photo_libraries()```
Returns list of Photos libraries found on the system
Returns list of Photos libraries found on the system. **Note**: On MacOS 10.15, this appears to list all libraries. On older systems, it may not find some libraries if they are not located in ~/Pictures. Provided for convenience but do not rely on this to find all libraries on the system.
### PhotosDB

View File

@@ -1,9 +1,10 @@
import glob
import json
import logging
import os.path
import platform
import subprocess
import sqlite3
import subprocess
import sys
import tempfile
import urllib.parse
@@ -192,15 +193,25 @@ def get_last_library_path():
def list_photo_libraries():
""" returns list of Photos libraries found on the system """
# lib_list = glob.glob(f"{str(Path.home())}/Pictures/*.photoslibrary")
# return lib_list
""" on MacOS < 10.15, this may omit some libraries """
# On 10.15, mdfind appears to find all libraries
# On older MacOS versions, mdfind appears to ignore some libraries
# glob to find libraries in ~/Pictures then mdfind to find all the others
lib_list = glob.glob(f"{str(Path.home())}/Pictures/*.photoslibrary")
# On older OS, may not get all libraries so make sure we get the last one
last_lib = get_last_library_path()
if last_lib:
lib_list.append(last_lib)
lib_list = []
output = subprocess.check_output(
["/usr/bin/mdfind", "-onlyin", "/", "-name", ".photoslibrary"]
).splitlines()
for lib in output:
lib_list.append(lib.decode("utf-8"))
lib_list = list(set(lib_list))
lib_list.sort()
return lib_list

View File

@@ -1,4 +1,4 @@
""" version info """
__version__ = "0.14.19"
__version__ = "0.14.20"

View File

@@ -29,14 +29,14 @@ CTX_SETTINGS = dict(help_option_names=["-h", "--help"])
required=False,
metavar="<Photos database path>",
default=None,
help="Specify database file",
help="Specify database file.",
)
@click.option(
"--json",
required=False,
is_flag=True,
default=False,
help="Print output in JSON format",
help="Print output in JSON format.",
)
@click.option("--debug", required=False, is_flag=True, default=False, hidden=True)
@click.version_option(__version__, "--version", "-v")
@@ -48,43 +48,43 @@ def cli(ctx, db, json, debug):
@cli.command()
@click.pass_obj
def keywords(cli_obj):
""" print out keywords found in the Photos library"""
""" Print out keywords found in the Photos library. """
photosdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
keywords = {"keywords": photosdb.keywords_as_dict()}
if cli_obj.json:
print(json.dumps(keywords))
click.echo(json.dumps(keywords))
else:
print(yaml.dump(keywords, sort_keys=False))
click.echo(yaml.dump(keywords, sort_keys=False))
@cli.command()
@click.pass_obj
def albums(cli_obj):
""" print out albums found in the Photos library """
""" Print out albums found in the Photos library. """
photosdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
albums = {"albums": photosdb.albums_as_dict()}
if cli_obj.json:
print(json.dumps(albums))
click.echo(json.dumps(albums))
else:
print(yaml.dump(albums, sort_keys=False))
click.echo(yaml.dump(albums, sort_keys=False))
@cli.command()
@click.pass_obj
def persons(cli_obj):
""" print out persons (faces) found in the Photos library """
""" Print out persons (faces) found in the Photos library. """
photosdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
persons = {"persons": photosdb.persons_as_dict()}
if cli_obj.json:
print(json.dumps(persons))
click.echo(json.dumps(persons))
else:
print(yaml.dump(persons, sort_keys=False))
click.echo(yaml.dump(persons, sort_keys=False))
@cli.command()
@click.pass_obj
def info(cli_obj):
""" print out descriptive info of the Photos library database """
""" Print out descriptive info of the Photos library database. """
pdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
info = {}
info["database_path"] = pdb.get_db_path()
@@ -117,57 +117,90 @@ def info(cli_obj):
info["persons"] = persons
if cli_obj.json:
print(json.dumps(info))
click.echo(json.dumps(info))
else:
print(yaml.dump(info, sort_keys=False))
click.echo(yaml.dump(info, sort_keys=False))
@cli.command()
@click.pass_obj
def dump(cli_obj):
""" print list of all photos & associated info from the Photos library """
""" Print list of all photos & associated info from the Photos library. """
pdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
photos = pdb.photos()
print_photo_info(photos, cli_obj.json)
@cli.command(name="list")
@click.pass_obj
def list_libraries(cli_obj):
""" Print list of Photos libraries found on the system. """
photo_libs = osxphotos.list_photo_libraries()
sys_lib = None
_, major, _ = osxphotos._get_os_version()
if int(major) >= 15:
sys_lib = osxphotos.get_system_library_path()
last_lib = osxphotos.get_last_library_path()
last_lib_flag = sys_lib_flag = False
for lib in photo_libs:
if lib == sys_lib:
click.echo(f"(*)\t{lib}")
sys_lib_flag = True
elif lib == last_lib:
click.echo(f"(#)\t{lib}")
last_lib_flag = True
else:
click.echo(f"\t{lib}")
if sys_lib_flag or last_lib_flag:
click.echo("\n")
if sys_lib_flag:
click.echo("(*)\tSystem Photos Library")
if last_lib_flag:
click.echo("(#)\tLast opened Photos Library")
@cli.command()
@click.option("--keyword", default=None, multiple=True, help="search for keyword(s)")
@click.option("--person", default=None, multiple=True, help="search for person(s)")
@click.option("--album", default=None, multiple=True, help="search for album(s)")
@click.option("--uuid", default=None, multiple=True, help="search for UUID(s)")
@click.option("--keyword", default=None, multiple=True, help="Search for keyword(s).")
@click.option("--person", default=None, multiple=True, help="Search for person(s).")
@click.option("--album", default=None, multiple=True, help="Search for album(s).")
@click.option("--uuid", default=None, multiple=True, help="Search for UUID(s).")
@click.option(
"--name", default=None, multiple=True, help="search for TEXT in name of photo"
"--name", default=None, multiple=True, help="Search for TEXT in name of photo."
)
@click.option("--no-name", is_flag=True, help="search for photos with no name")
@click.option("--no-name", is_flag=True, help="Search for photos with no name.")
@click.option(
"--description",
default=None,
multiple=True,
help="search for TEXT in description of photo",
help="Search for TEXT in description of photo.",
)
@click.option(
"--no-description", is_flag=True, help="search for photos with no description"
"--no-description", is_flag=True, help="Search for photos with no description."
)
@click.option(
"-i",
"--ignore-case",
is_flag=True,
help="case insensitive search for name or description. Does not apply to keyword, person, or album",
help="Case insensitive search for name or description. Does not apply to keyword, person, or album.",
)
@click.option("--edited", is_flag=True, help="search for photos that have been edited")
@click.option("--external-edit", is_flag=True, help="search for photos edited in external editor")
@click.option("--favorite", is_flag=True, help="search for photos marked favorite")
@click.option("--edited", is_flag=True, help="Search for photos that have been edited.")
@click.option(
"--not-favorite", is_flag=True, help="search for photos not marked favorite"
"--external-edit", is_flag=True, help="Search for photos edited in external editor."
)
@click.option("--hidden", is_flag=True, help="search for photos marked hidden")
@click.option("--not-hidden", is_flag=True, help="search for photos not marked hidden")
@click.option("--missing", is_flag=True, help="search for photos missing from disk")
@click.option("--favorite", is_flag=True, help="Search for photos marked favorite.")
@click.option(
"--not-favorite", is_flag=True, help="Search for photos not marked favorite."
)
@click.option("--hidden", is_flag=True, help="Search for photos marked hidden.")
@click.option("--not-hidden", is_flag=True, help="Search for photos not marked hidden.")
@click.option("--missing", is_flag=True, help="Search for photos missing from disk.")
@click.option(
"--not-missing",
is_flag=True,
help="search for photos present on disk (e.g. not missing)",
help="Search for photos present on disk (e.g. not missing).",
)
@click.option(
"--json",
@@ -200,9 +233,9 @@ def query(
missing,
not_missing,
):
""" query the Photos database using 1 or more search options;
""" Query the Photos database using 1 or more search options;
if more than one option is provided, they are treated as "AND"
(e.g. search for photos matching all options)
(e.g. search for photos matching all options).
"""
# if no query terms, show help and return
@@ -226,27 +259,27 @@ def query(
not_missing,
]
):
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
elif favorite and not_favorite:
# can't search for both favorite and notfavorite
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
elif hidden and not_hidden:
# can't search for both hidden and nothidden
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
elif missing and not_missing:
# can't search for both missing and notmissing
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
elif name and no_name:
# can't search for both name and no_name
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
elif description and no_description:
# can't search for both description and no_description
print(cli.commands["query"].get_help(ctx))
click.echo(cli.commands["query"].get_help(ctx))
return
else:
photosdb = osxphotos.PhotosDB(dbfile=cli_obj.db)
@@ -316,11 +349,11 @@ def query(
@click.argument("topic", default=None, required=False, nargs=1)
@click.pass_context
def help(ctx, topic, **kw):
""" print help; for help on commands: help <command> """
""" Print help; for help on commands: help <command>. """
if topic is None:
print(ctx.parent.get_help())
click.echo(ctx.parent.get_help())
else:
print(cli.commands[topic].get_help(ctx))
click.echo(cli.commands[topic].get_help(ctx))
def print_photo_info(photos, json=False):
@@ -328,7 +361,7 @@ def print_photo_info(photos, json=False):
dump = []
for p in photos:
dump.append(p.to_json())
print(f"[{', '.join(dump)}]")
click.echo(f"[{', '.join(dump)}]")
else:
# dump as CSV
csv_writer = csv.writer(