Added tutorial, closes #432
This commit is contained in:
11
README.md
11
README.md
@@ -50,11 +50,14 @@ OSXPhotos provides the ability to interact with and query Apple's Photos.app lib
|
|||||||
|
|
||||||
## Supported operating systems
|
## Supported operating systems
|
||||||
|
|
||||||
Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) until macOS Big Sur (10.16/11.1).
|
Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) until macOS Big Sur (10.16/11.3).
|
||||||
|
|
||||||
|
If you have access to the macOS 12 / Monterey beta and would like to help ensure osxphotos is compatible, please visit the [Discussions](https://github.com/RhetTbull/osxphotos/discussions) page and let me know!
|
||||||
|
|
||||||
| macOS Version | macOS name | Photos.app version |
|
| macOS Version | macOS name | Photos.app version |
|
||||||
| ----------------- |------------|:-------------------|
|
| ----------------- |------------|:-------------------|
|
||||||
| 10.16, 11.0-11.3 | Big Sur | 6.0 ✅ |
|
| 12.0 | Monterey | ?.0 UNKNOWN |
|
||||||
|
| 10.16, 11.0-11.4 | Big Sur | 6.0 ✅ |
|
||||||
| 10.15.1 - 10.15.7 | Catalina | 5.0 ✅ |
|
| 10.15.1 - 10.15.7 | Catalina | 5.0 ✅ |
|
||||||
| 10.14.5, 10.14.6 | Mojave | 4.0 ✅ |
|
| 10.14.5, 10.14.6 | Mojave | 4.0 ✅ |
|
||||||
| 10.13.6 | High Sierra| 3.0 ✅ |
|
| 10.13.6 | High Sierra| 3.0 ✅ |
|
||||||
@@ -1565,7 +1568,7 @@ Substitution Description
|
|||||||
{lf} A line feed: '\n', alias for {newline}
|
{lf} A line feed: '\n', alias for {newline}
|
||||||
{cr} A carriage return: '\r'
|
{cr} A carriage return: '\r'
|
||||||
{crlf} a carriage return + line feed: '\r\n'
|
{crlf} a carriage return + line feed: '\r\n'
|
||||||
{osxphotos_version} The osxphotos version, e.g. '0.42.39'
|
{osxphotos_version} The osxphotos version, e.g. '0.42.40'
|
||||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||||
|
|
||||||
The following substitutions may result in multiple values. Thus if specified for
|
The following substitutions may result in multiple values. Thus if specified for
|
||||||
@@ -3353,7 +3356,7 @@ The following template field substitutions are availabe for use the templating s
|
|||||||
|{lf}|A line feed: '\n', alias for {newline}|
|
|{lf}|A line feed: '\n', alias for {newline}|
|
||||||
|{cr}|A carriage return: '\r'|
|
|{cr}|A carriage return: '\r'|
|
||||||
|{crlf}|a carriage return + line feed: '\r\n'|
|
|{crlf}|a carriage return + line feed: '\r\n'|
|
||||||
|{osxphotos_version}|The osxphotos version, e.g. '0.42.39'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.42.40'|
|
||||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||||
|{album}|Album(s) photo is contained in|
|
|{album}|Album(s) photo is contained in|
|
||||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ Supported operating systems
|
|||||||
|
|
||||||
Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through macOS Big Sur (11.3).
|
Only works on macOS (aka Mac OS X). Tested on macOS Sierra (10.12.6) through macOS Big Sur (11.3).
|
||||||
|
|
||||||
|
If you have access to macOS 12 / Monterey beta and would like to help ensure osxphotos is compatible, please contact me via GitHub.
|
||||||
|
|
||||||
This package will read Photos databases for any supported version on any supported macOS version.
|
This package will read Photos databases for any supported version on any supported macOS version.
|
||||||
E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.
|
E.g. you can read a database created with Photos 5.0 on MacOS 10.15 on a machine running macOS 10.12 and vice versa.
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ Alternatively, you can also run the command line utility like this: ``python3 -m
|
|||||||
persons Print out persons (faces) found in the Photos library.
|
persons Print out persons (faces) found in the Photos library.
|
||||||
places Print out places found in the Photos library.
|
places Print out places found in the Photos library.
|
||||||
query Query the Photos database using 1 or more search options; if...
|
query Query the Photos database using 1 or more search options; if...
|
||||||
|
tutorial Display osxphotos tutorial.
|
||||||
|
|
||||||
To get help on a specific command, use ``osxphotos help <command_name>``
|
To get help on a specific command, use ``osxphotos help <command_name>``
|
||||||
|
|
||||||
|
|||||||
@@ -8,41 +8,50 @@ import importlib
|
|||||||
pathex = os.getcwd()
|
pathex = os.getcwd()
|
||||||
|
|
||||||
# include necessary data files
|
# include necessary data files
|
||||||
datas=[('osxphotos/templates/xmp_sidecar.mako', 'osxphotos/templates'), ('osxphotos/templates/xmp_sidecar_beta.mako', 'osxphotos/templates'), ('osxphotos/phototemplate.tx', 'osxphotos'), ('osxphotos/phototemplate.md', 'osxphotos')]
|
datas = [
|
||||||
package_imports = [['photoscript', ['photoscript.applescript']]]
|
("osxphotos/templates/xmp_sidecar.mako", "osxphotos/templates"),
|
||||||
|
("osxphotos/templates/xmp_sidecar_beta.mako", "osxphotos/templates"),
|
||||||
|
("osxphotos/phototemplate.tx", "osxphotos"),
|
||||||
|
("osxphotos/phototemplate.md", "osxphotos"),
|
||||||
|
("osxphotos/tutorial.md", "osxphotos"),
|
||||||
|
]
|
||||||
|
package_imports = [["photoscript", ["photoscript.applescript"]]]
|
||||||
for package, files in package_imports:
|
for package, files in package_imports:
|
||||||
proot = os.path.dirname(importlib.import_module(package).__file__)
|
proot = os.path.dirname(importlib.import_module(package).__file__)
|
||||||
datas.extend((os.path.join(proot, f), package) for f in files)
|
datas.extend((os.path.join(proot, f), package) for f in files)
|
||||||
|
|
||||||
block_cipher = None
|
block_cipher = None
|
||||||
|
|
||||||
a = Analysis(['cli.py'],
|
a = Analysis(
|
||||||
pathex=[pathex],
|
["cli.py"],
|
||||||
binaries=[],
|
pathex=[pathex],
|
||||||
datas=datas,
|
binaries=[],
|
||||||
hiddenimports=['pkg_resources.py2_warn'],
|
datas=datas,
|
||||||
hookspath=[],
|
hiddenimports=["pkg_resources.py2_warn"],
|
||||||
runtime_hooks=[],
|
hookspath=[],
|
||||||
excludes=[],
|
runtime_hooks=[],
|
||||||
win_no_prefer_redirects=False,
|
excludes=[],
|
||||||
win_private_assemblies=False,
|
win_no_prefer_redirects=False,
|
||||||
cipher=block_cipher,
|
win_private_assemblies=False,
|
||||||
noarchive=False)
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data,
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(pyz,
|
exe = EXE(
|
||||||
a.scripts,
|
pyz,
|
||||||
a.binaries,
|
a.scripts,
|
||||||
a.zipfiles,
|
a.binaries,
|
||||||
a.datas,
|
a.zipfiles,
|
||||||
[],
|
a.datas,
|
||||||
name='osxphotos',
|
[],
|
||||||
debug=False,
|
name="osxphotos",
|
||||||
bootloader_ignore_signals=False,
|
debug=False,
|
||||||
strip=False,
|
bootloader_ignore_signals=False,
|
||||||
upx=True,
|
strip=False,
|
||||||
upx_exclude=[],
|
upx=True,
|
||||||
runtime_tmpdir=None,
|
upx_exclude=[],
|
||||||
console=True )
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
)
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ _TESTED_OS_VERSIONS = [
|
|||||||
("11", "1"),
|
("11", "1"),
|
||||||
("11", "2"),
|
("11", "2"),
|
||||||
("11", "3"),
|
("11", "3"),
|
||||||
|
("11", "4"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Photos 5 has persons who are empty string if unidentified face
|
# Photos 5 has persons who are empty string if unidentified face
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.42.39"
|
__version__ = "0.42.40"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ from ._constants import (
|
|||||||
SIDECAR_XMP,
|
SIDECAR_XMP,
|
||||||
)
|
)
|
||||||
from ._version import __version__
|
from ._version import __version__
|
||||||
from .cli_help import ExportCommand
|
from .cli_help import ExportCommand, tutorial_help
|
||||||
from .configoptions import (
|
from .configoptions import (
|
||||||
ConfigOptions,
|
ConfigOptions,
|
||||||
ConfigOptionsInvalidError,
|
ConfigOptionsInvalidError,
|
||||||
@@ -3759,3 +3759,17 @@ SOFTWARE.
|
|||||||
click.echo("")
|
click.echo("")
|
||||||
click.echo(f"Source code available at: {OSXPHOTOS_URL}")
|
click.echo(f"Source code available at: {OSXPHOTOS_URL}")
|
||||||
click.echo(license)
|
click.echo(license)
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(name="tutorial")
|
||||||
|
@click.argument(
|
||||||
|
"WIDTH",
|
||||||
|
nargs=-1,
|
||||||
|
type=click.INT,
|
||||||
|
)
|
||||||
|
@click.pass_obj
|
||||||
|
@click.pass_context
|
||||||
|
def tutorial(ctx, cli_obj, width):
|
||||||
|
"""Display osxphotos tutorial."""
|
||||||
|
width = width[0] if width else 100
|
||||||
|
click.echo_via_pager(tutorial_help(width=width))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Help text helper class for osxphotos CLI """
|
"""Help text helper class for osxphotos CLI """
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import click
|
import click
|
||||||
@@ -250,13 +251,26 @@ def template_help(width=78):
|
|||||||
"""Return formatted string for template system"""
|
"""Return formatted string for template system"""
|
||||||
sio = io.StringIO()
|
sio = io.StringIO()
|
||||||
console = Console(file=sio, force_terminal=True, width=width)
|
console = Console(file=sio, force_terminal=True, width=width)
|
||||||
template_help_md = strip_md_links(get_template_help())
|
template_help_md = strip_md_header_and_links(get_template_help())
|
||||||
console.print(Markdown(template_help_md))
|
console.print(Markdown(template_help_md))
|
||||||
help_str = sio.getvalue()
|
help_str = sio.getvalue()
|
||||||
sio.close()
|
sio.close()
|
||||||
return help_str
|
return help_str
|
||||||
|
|
||||||
|
|
||||||
|
def tutorial_help(width=78):
|
||||||
|
"""Return formatted string for tutorial"""
|
||||||
|
sio = io.StringIO()
|
||||||
|
console = Console(file=sio, force_terminal=True, width=width)
|
||||||
|
help_md = get_tutorial_text()
|
||||||
|
help_md = strip_html_comments(help_md)
|
||||||
|
help_md = strip_md_links(help_md)
|
||||||
|
console.print(Markdown(help_md))
|
||||||
|
help_str = sio.getvalue()
|
||||||
|
sio.close()
|
||||||
|
return help_str
|
||||||
|
|
||||||
|
|
||||||
def rich_text(text, width=78):
|
def rich_text(text, width=78):
|
||||||
"""Return rich formatted text"""
|
"""Return rich formatted text"""
|
||||||
sio = io.StringIO()
|
sio = io.StringIO()
|
||||||
@@ -267,6 +281,26 @@ def rich_text(text, width=78):
|
|||||||
return rich_text
|
return rich_text
|
||||||
|
|
||||||
|
|
||||||
|
def strip_md_header_and_links(md):
|
||||||
|
"""strip markdown headers and links from markdown text md
|
||||||
|
|
||||||
|
Args:
|
||||||
|
md: str, markdown text
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str with markdown headers and links removed
|
||||||
|
|
||||||
|
Note: This uses a very basic regex that likely fails on all sorts of edge cases
|
||||||
|
but works for the links in the osxphotos docs
|
||||||
|
"""
|
||||||
|
links = r"(?:[*#])|\[(.*?)\]\(.+?\)"
|
||||||
|
|
||||||
|
def subfn(match):
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
return re.sub(links, subfn, md)
|
||||||
|
|
||||||
|
|
||||||
def strip_md_links(md):
|
def strip_md_links(md):
|
||||||
"""strip markdown links from markdown text md
|
"""strip markdown links from markdown text md
|
||||||
|
|
||||||
@@ -279,9 +313,23 @@ def strip_md_links(md):
|
|||||||
Note: This uses a very basic regex that likely fails on all sorts of edge cases
|
Note: This uses a very basic regex that likely fails on all sorts of edge cases
|
||||||
but works for the links in the osxphotos docs
|
but works for the links in the osxphotos docs
|
||||||
"""
|
"""
|
||||||
links = r"(?:[*#])|\[(.*?)\]\(.+?\)"
|
links = r"\[(.*?)\]\(.+?\)"
|
||||||
|
|
||||||
def subfn(match):
|
def subfn(match):
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
|
|
||||||
return re.sub(links, subfn, md)
|
return re.sub(links, subfn, md)
|
||||||
|
|
||||||
|
|
||||||
|
def strip_html_comments(text):
|
||||||
|
"""Strip html comments from text (which doesn't need to be valid HTML)"""
|
||||||
|
return re.sub(r"<!--(.|\s|\n)*?-->", "", text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_tutorial_text():
|
||||||
|
"""Load tutorial text from file"""
|
||||||
|
# TODO: would be better to use importlib.abc.ResourceReader but I can't find a single example of how to do this
|
||||||
|
help_file = pathlib.Path(__file__).parent / "tutorial.md"
|
||||||
|
with open(help_file, "r") as fd:
|
||||||
|
md = fd.read()
|
||||||
|
return md
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from osxphotos.phototemplate import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATE_HELP = "osxphotos/phototemplate.md"
|
TEMPLATE_HELP = "osxphotos/phototemplate.md"
|
||||||
TUTORIAL_HELP = "docsrc/source/tutorial.md"
|
TUTORIAL_HELP = "osxphotos/tutorial.md"
|
||||||
|
|
||||||
USAGE_START = (
|
USAGE_START = (
|
||||||
"<!-- OSXPHOTOS-EXPORT-USAGE:START - Do not remove or modify this section -->"
|
"<!-- OSXPHOTOS-EXPORT-USAGE:START - Do not remove or modify this section -->"
|
||||||
|
|||||||
Reference in New Issue
Block a user