Added list option to cmd_line. Closes #14
This commit is contained in:
59
README.md
59
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.14.19"
|
||||
__version__ = "0.14.20"
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -290,7 +323,7 @@ def query(
|
||||
|
||||
if edited:
|
||||
photos = [p for p in photos if p.hasadjustments()]
|
||||
|
||||
|
||||
if external_edit:
|
||||
photos = [p for p in photos if p.external_edit()]
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user