Updated REPL, now with more cowbell
This commit is contained in:
@@ -1702,7 +1702,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.89'
|
{osxphotos_version} The osxphotos version, e.g. '0.42.90'
|
||||||
{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
|
||||||
@@ -3573,7 +3573,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.89'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.42.90'|
|
||||||
|{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|
|
||||||
@@ -3838,6 +3838,8 @@ For additional details about how osxphotos is implemented or if you would like t
|
|||||||
- [textx](https://github.com/textX/textX)
|
- [textx](https://github.com/textX/textX)
|
||||||
- [bitmath](https://github.com/tbielawa/bitmath)
|
- [bitmath](https://github.com/tbielawa/bitmath)
|
||||||
- [more-itertools](https://github.com/more-itertools/more-itertools)
|
- [more-itertools](https://github.com/more-itertools/more-itertools)
|
||||||
|
- [ptpython](https://github.com/prompt-toolkit/ptpython)
|
||||||
|
- [objexplore](https://github.com/kylepollina/objexplore)
|
||||||
|
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.42.89"
|
__version__ = "0.42.90"
|
||||||
|
|
||||||
|
|||||||
106
osxphotos/cli.py
106
osxphotos/cli.py
@@ -55,10 +55,11 @@ from .exiftool import get_exiftool_path
|
|||||||
from .export_db import ExportDB, ExportDBInMemory
|
from .export_db import ExportDB, ExportDBInMemory
|
||||||
from .fileutil import FileUtil, FileUtilNoOp
|
from .fileutil import FileUtil, FileUtilNoOp
|
||||||
from .path_utils import is_valid_filepath, sanitize_filename, sanitize_filepath
|
from .path_utils import is_valid_filepath, sanitize_filename, sanitize_filepath
|
||||||
from .photoinfo import ExportResults
|
from .photoinfo import ExportResults, PhotoInfo
|
||||||
from .photokit import check_photokit_authorization, request_photokit_authorization
|
from .photokit import check_photokit_authorization, request_photokit_authorization
|
||||||
from .photosalbum import PhotosAlbum
|
from .photosalbum import PhotosAlbum
|
||||||
from .phototemplate import PhotoTemplate, RenderOptions
|
from .phototemplate import PhotoTemplate, RenderOptions
|
||||||
|
from .pyrepl import embed_repl
|
||||||
from .queryoptions import QueryOptions
|
from .queryoptions import QueryOptions
|
||||||
from .uti import get_preferred_uti_extension
|
from .uti import get_preferred_uti_extension
|
||||||
from .utils import expand_and_validate_filepath, load_function
|
from .utils import expand_and_validate_filepath, load_function
|
||||||
@@ -4011,6 +4012,32 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
osxphotos uses the following 3rd party software licensed under the BSD-3-Clause License:
|
||||||
|
Click (Copyright 2014 Pallets), ptpython (Copyright (c) 2015, Jonathan Slenders)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list
|
||||||
|
of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
used to endorse or promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
|
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
"""
|
"""
|
||||||
click.echo(f"osxphotos, version {__version__}")
|
click.echo(f"osxphotos, version {__version__}")
|
||||||
click.echo("")
|
click.echo("")
|
||||||
@@ -4032,7 +4059,7 @@ def tutorial(ctx, cli_obj, width):
|
|||||||
click.echo_via_pager(tutorial_help(width=width))
|
click.echo_via_pager(tutorial_help(width=width))
|
||||||
|
|
||||||
|
|
||||||
def _show_photo(photo):
|
def _show_photo(photo: PhotoInfo):
|
||||||
"""open image with default image viewer
|
"""open image with default image viewer
|
||||||
|
|
||||||
Note: This is for debugging only -- it will actually open any filetype which could
|
Note: This is for debugging only -- it will actually open any filetype which could
|
||||||
@@ -4078,16 +4105,34 @@ def _get_selected(photosdb):
|
|||||||
return get_selected
|
return get_selected
|
||||||
|
|
||||||
|
|
||||||
|
def _spotlight_photo(photo: PhotoInfo):
|
||||||
|
photo_ = photoscript.Photo(photo.uuid)
|
||||||
|
photo_.spotlight()
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@DB_OPTION
|
@DB_OPTION
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def repl(ctx, cli_obj, db):
|
@click.option(
|
||||||
"""Run interactive osxphotos shell"""
|
"--emacs",
|
||||||
|
required=False,
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Launch REPL with Emacs keybindings (default is vi bindings)",
|
||||||
|
)
|
||||||
|
def repl(ctx, cli_obj, db, emacs):
|
||||||
|
"""Run interactive osxphotos REPL shell (useful for debugging, prototyping, and inspecting your Photos library)"""
|
||||||
|
|
||||||
from osxphotos import PhotosDB, PhotoInfo, ExifTool
|
from objexplore import explore
|
||||||
|
from photoscript import Album, Photo, PhotosLibrary
|
||||||
from rich import inspect as _inspect
|
from rich import inspect as _inspect
|
||||||
|
|
||||||
|
from osxphotos import ExifTool, PhotoInfo, PhotosDB
|
||||||
|
from osxphotos.albuminfo import AlbumInfo
|
||||||
|
from osxphotos.placeinfo import PlaceInfo
|
||||||
|
from osxphotos.queryoptions import QueryOptions
|
||||||
|
|
||||||
pretty.install()
|
pretty.install()
|
||||||
print(f"python version: {sys.version}")
|
print(f"python version: {sys.version}")
|
||||||
print(f"osxphotos version: {osxphotos._version.__version__}")
|
print(f"osxphotos version: {osxphotos._version.__version__}")
|
||||||
@@ -4102,6 +4147,7 @@ def repl(ctx, cli_obj, db):
|
|||||||
# shortcut for helper functions
|
# shortcut for helper functions
|
||||||
get_photo = photosdb.get_photo
|
get_photo = photosdb.get_photo
|
||||||
show = _show_photo
|
show = _show_photo
|
||||||
|
spotlight = _spotlight_photo
|
||||||
get_selected = _get_selected(photosdb)
|
get_selected = _get_selected(photosdb)
|
||||||
try:
|
try:
|
||||||
selected = get_selected()
|
selected = get_selected()
|
||||||
@@ -4113,17 +4159,9 @@ def repl(ctx, cli_obj, db):
|
|||||||
"""inspect object"""
|
"""inspect object"""
|
||||||
return _inspect(obj, methods=True)
|
return _inspect(obj, methods=True)
|
||||||
|
|
||||||
class ReprQuit:
|
print(f"Found {len(photos)} photos in {tictoc:0.2f} seconds\n")
|
||||||
def __repr__(self):
|
print("The following classes have been imported from osxphotos:")
|
||||||
sys.exit(0)
|
print("- AlbumInfo, ExifTool, PhotoInfo, PhotosDB, PlaceInfo, QueryOptions\n")
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
quit = ReprQuit()
|
|
||||||
q = ReprQuit()
|
|
||||||
|
|
||||||
print(f"Found {len(photos)} photos in {tictoc:0.2f} seconds")
|
|
||||||
print("The following variables are defined:")
|
print("The following variables are defined:")
|
||||||
print(f"- photosdb: PhotosDB() instance for {photosdb.library_path}")
|
print(f"- photosdb: PhotosDB() instance for {photosdb.library_path}")
|
||||||
print(
|
print(
|
||||||
@@ -4133,16 +4171,34 @@ def repl(ctx, cli_obj, db):
|
|||||||
f"- selected: list of PhotoInfo objects for any photos selected in Photos (len={len(selected)})"
|
f"- selected: list of PhotoInfo objects for any photos selected in Photos (len={len(selected)})"
|
||||||
)
|
)
|
||||||
print(f"\nThe following functions may be helpful:")
|
print(f"\nThe following functions may be helpful:")
|
||||||
print(f"- get_photo(uuid): return a PhotoInfo object for photo with uuid")
|
|
||||||
print(
|
print(
|
||||||
f"- get_selected(): return list of PhotoInfo objects for photos selected in Photos"
|
f"- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo('B13F4485-94E0-41CD-AF71-913095D62E31')"
|
||||||
)
|
|
||||||
print(f"- show(photo): open a photo object in the default viewer")
|
|
||||||
print(
|
|
||||||
f"- help(object): print help text including list of methods for object; for example, help(PhotosDB)"
|
|
||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
f"- inspect(object): print information about an object; for example inspect(photosdb)"
|
f"- get_selected(); return list of PhotoInfo objects for photos selected in Photos"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"- show(photo): open a photo object in the default viewer; e.g. show(selected[0])"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"- show(path): open a file at path in the default viewer; e.g. show('/path/to/photo.jpg')"
|
||||||
|
)
|
||||||
|
print(f"- spotlight(photo): open a photo and spotlight it in Photos")
|
||||||
|
# print(
|
||||||
|
# f"- help(object): print help text including list of methods for object; for example, help(PhotosDB)"
|
||||||
|
# )
|
||||||
|
print(
|
||||||
|
f"- inspect(object): print information about an object; e.g. inspect(PhotoInfo)"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"- explore(object): interactively explore an object with objexplore; e.g. explore(PhotoInfo)"
|
||||||
|
)
|
||||||
|
print(f"- q, quit, quit(), exit, exit(): exit this interactive shell\n")
|
||||||
|
|
||||||
|
embed_repl(
|
||||||
|
globals=globals(),
|
||||||
|
locals=locals(),
|
||||||
|
history_filename=str(pathlib.Path.home() / ".osxphotos_repl_history"),
|
||||||
|
quit_words=["q", "quit", "exit"],
|
||||||
|
vi_mode=not emacs,
|
||||||
)
|
)
|
||||||
print(f"- q, quit, or quit(): exit this interactive shell\n")
|
|
||||||
code.interact(banner="", local=locals())
|
|
||||||
|
|||||||
131
osxphotos/pyrepl.py
Normal file
131
osxphotos/pyrepl.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
""" Custom Python REPL based on ptpython that allows quitting with custom keywords instead of `quit()` """
|
||||||
|
|
||||||
|
""" This file is distributed under the same license as the ptpython package:
|
||||||
|
|
||||||
|
Copyright (c) 2015, Jonathan Slenders (ptpython), (c) 2021 Rhet Turnbull (this file)
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the {organization} nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from typing import Callable, List, Optional
|
||||||
|
|
||||||
|
from ptpython.repl import (
|
||||||
|
ContextManager,
|
||||||
|
DummyContext,
|
||||||
|
PythonRepl,
|
||||||
|
builtins,
|
||||||
|
patch_stdout_context,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PyReplQuitter(PythonRepl):
|
||||||
|
"""Custom pypython repl that allows quitting REPL with custom commands"""
|
||||||
|
|
||||||
|
def __init__(self, *args, quit_words: Optional[List[str]] = None, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.quit_words = quit_words or ["quit", "q"]
|
||||||
|
|
||||||
|
def eval(self, line: str) -> object:
|
||||||
|
if line.strip() in self.quit_words:
|
||||||
|
sys.exit(0)
|
||||||
|
return super().eval(line)
|
||||||
|
|
||||||
|
|
||||||
|
def embed_repl(
|
||||||
|
globals=None,
|
||||||
|
locals=None,
|
||||||
|
configure: Optional[Callable[[PythonRepl], None]] = None,
|
||||||
|
vi_mode: bool = False,
|
||||||
|
history_filename: Optional[str] = None,
|
||||||
|
title: Optional[str] = None,
|
||||||
|
startup_paths=None,
|
||||||
|
patch_stdout: bool = False,
|
||||||
|
return_asyncio_coroutine: bool = False,
|
||||||
|
quit_words: Optional[List[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Call this to embed Python shell at the current point in your program.
|
||||||
|
It's similar to `IPython.embed` and `bpython.embed`. ::
|
||||||
|
from prompt_toolkit.contrib.repl import embed
|
||||||
|
embed(globals(), locals())
|
||||||
|
:param vi_mode: Boolean. Use Vi instead of Emacs key bindings.
|
||||||
|
:param configure: Callable that will be called with the `PythonRepl` as a first
|
||||||
|
argument, to trigger configuration.
|
||||||
|
:param title: Title to be displayed in the terminal titlebar. (None or string.)
|
||||||
|
:param patch_stdout: When true, patch `sys.stdout` so that background
|
||||||
|
threads that are printing will print nicely above the prompt.
|
||||||
|
"""
|
||||||
|
# Default globals/locals
|
||||||
|
if globals is None:
|
||||||
|
globals = {
|
||||||
|
"__name__": "__main__",
|
||||||
|
"__package__": None,
|
||||||
|
"__doc__": None,
|
||||||
|
"__builtins__": builtins,
|
||||||
|
}
|
||||||
|
|
||||||
|
locals = locals or globals
|
||||||
|
|
||||||
|
def get_globals():
|
||||||
|
return globals
|
||||||
|
|
||||||
|
def get_locals():
|
||||||
|
return locals
|
||||||
|
|
||||||
|
# Create REPL.
|
||||||
|
repl = PyReplQuitter(
|
||||||
|
get_globals=get_globals,
|
||||||
|
get_locals=get_locals,
|
||||||
|
vi_mode=vi_mode,
|
||||||
|
history_filename=history_filename,
|
||||||
|
startup_paths=startup_paths,
|
||||||
|
quit_words=quit_words,
|
||||||
|
)
|
||||||
|
|
||||||
|
if title:
|
||||||
|
repl.terminal_title = title
|
||||||
|
|
||||||
|
if configure:
|
||||||
|
configure(repl)
|
||||||
|
|
||||||
|
# Start repl.
|
||||||
|
patch_context: ContextManager = (
|
||||||
|
patch_stdout_context() if patch_stdout else DummyContext()
|
||||||
|
)
|
||||||
|
|
||||||
|
if return_asyncio_coroutine:
|
||||||
|
|
||||||
|
async def coroutine():
|
||||||
|
with patch_context:
|
||||||
|
await repl.run_async()
|
||||||
|
|
||||||
|
return coroutine()
|
||||||
|
else:
|
||||||
|
with patch_context:
|
||||||
|
repl.run()
|
||||||
@@ -18,6 +18,8 @@ photoscript>=0.1.4,<0.2.0
|
|||||||
toml>=0.10.2,<0.11.0
|
toml>=0.10.2,<0.11.0
|
||||||
osxmetadata>=0.99.33,<1.0.0
|
osxmetadata>=0.99.33,<1.0.0
|
||||||
textx>=2.3.0,<2.4.0
|
textx>=2.3.0,<2.4.0
|
||||||
rich>=10.6.0,<11.0.0
|
rich>=10.6.0,<=11.0.0
|
||||||
bitmath>=1.3.3.1,<1.4.0.0
|
bitmath>=1.3.3.1,<1.4.0.0
|
||||||
more-itertools>=8.8.0,<9.0.0
|
more-itertools>=8.8.0,<9.0.0
|
||||||
|
objexplore>=1.5.5,<1.6.0
|
||||||
|
ptpython>=3.0.20,<3.1.0
|
||||||
4
setup.py
4
setup.py
@@ -93,9 +93,11 @@ setup(
|
|||||||
"toml>=0.10.2,<0.11.0",
|
"toml>=0.10.2,<0.11.0",
|
||||||
"osxmetadata>=0.99.33,<1.0.0",
|
"osxmetadata>=0.99.33,<1.0.0",
|
||||||
"textx>=2.3.0,<2.4.0",
|
"textx>=2.3.0,<2.4.0",
|
||||||
"rich>=10.6.0,<11.0.0",
|
"rich>=10.6.0,<=11.0.0",
|
||||||
"bitmath>=1.3.3.1,<1.4.0.0",
|
"bitmath>=1.3.3.1,<1.4.0.0",
|
||||||
"more-itertools>=8.8.0,<9.0.0",
|
"more-itertools>=8.8.0,<9.0.0",
|
||||||
|
"objexplore>=1.5.5,<1.6.0",
|
||||||
|
"ptpython>=3.0.20,<3.1.0",
|
||||||
],
|
],
|
||||||
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]},
|
entry_points={"console_scripts": ["osxphotos=osxphotos.__main__:cli"]},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ pytestmark = pytest.mark.skipif(
|
|||||||
)
|
)
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
"has_adjustments": "C925CFDC-FF2B-4E71-AC9D-C669B6453A8B",
|
"has_adjustments": "C925CFDC-FF2B-4E71-AC9D-C669B6453A8B", # IMG_1929.JPG
|
||||||
"no_adjustments": "16A6AF6B-D8FC-4256-AE33-889733E3EEAB",
|
"no_adjustments": "16A6AF6B-D8FC-4256-AE33-889733E3EEAB", # IMG_9847.JPG
|
||||||
"live": "8EC216A2-0032-4934-BD3F-04C6259B3304",
|
"live": "8EC216A2-0032-4934-BD3F-04C6259B3304", # IMG_3259.HEIC
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID_BURSTS = {
|
UUID_BURSTS = {
|
||||||
|
|||||||
Reference in New Issue
Block a user