Add photo conversion
This commit is contained in:
parent
c09179f5da
commit
3e7ce98755
10 changed files with 215 additions and 54 deletions
|
@ -15,7 +15,7 @@ classifiers = [
|
|||
"Topic :: System :: Distributed Computing"
|
||||
]
|
||||
keywords = ["Personal tools and recipes"]
|
||||
dependencies = ["pydantic", "tinytag"]
|
||||
dependencies = ["pydantic", "tinytag", "pillow", "pillow_heif"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://git.jmsgrogan.com/jgrogan/recipes"
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Callable
|
|||
import logging
|
||||
|
||||
from jgutils.filesystem import get_files_recursive
|
||||
from jgutils.tasks import run_tasks
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -19,19 +20,29 @@ def _get_converted_path(input_path: Path,
|
|||
output_filename = str(input_path.stem) + f".{output_ext}"
|
||||
return output_dir / input_path.parent / output_filename
|
||||
|
||||
def convert(input_dir: Path,
|
||||
def _should_convert(overwrite_existing, path):
|
||||
if overwrite_existing:
|
||||
return True
|
||||
return not path.exists()
|
||||
|
||||
def convert(input_path: Path,
|
||||
output_dir: Path,
|
||||
input_ext: str,
|
||||
output_ext: str,
|
||||
conversion_func: Callable):
|
||||
conversion_func: Callable,
|
||||
overwrite_existing: bool = False):
|
||||
|
||||
logger.info("Converting files in %s to %s", input_dir, output_dir)
|
||||
logger.info("Converting files in %s to %s", input_path, output_dir)
|
||||
|
||||
input_files = get_files_recursive(input_dir.resolve(), [input_ext])
|
||||
output_files = [_get_converted_path(f.relative_to(input_dir),
|
||||
if input_path.is_dir():
|
||||
input_files = get_files_recursive(input_path.resolve(), [input_ext])
|
||||
else:
|
||||
input_files = [input_path]
|
||||
output_files = [_get_converted_path(f.relative_to(input_path),
|
||||
output_dir,
|
||||
output_ext) for f in input_files]
|
||||
conversion_pairs = [io_paths for io_paths in
|
||||
zip(input_files, output_files) if not io_paths[1].exists()]
|
||||
zip(input_files, output_files)
|
||||
if _should_convert(overwrite_existing, io_paths[1])]
|
||||
|
||||
run_tasks(conversion_func, conversion_pairs)
|
||||
|
|
65
src/jgutils/filesystem/cli.py
Normal file
65
src/jgutils/filesystem/cli.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import logging
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
import shutil
|
||||
|
||||
from .filesystem import for_each_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def _remove_file(path: Path):
|
||||
path.unlink()
|
||||
|
||||
def cli_file_remove(args):
|
||||
|
||||
input_path = args.path.resolve()
|
||||
extension = args.ext
|
||||
|
||||
logger.info("Removing files with extension %s from %s",
|
||||
extension, input_path)
|
||||
for_each_file(input_path, [extension], _remove_file)
|
||||
|
||||
|
||||
def _flatten(parent: Path, path: Path):
|
||||
relative_path = path.relative_to(parent)
|
||||
relative_dir = relative_path.parent
|
||||
|
||||
new_filename = "_".join(relative_dir.parts)
|
||||
new_filename += "_" + path.name
|
||||
new_path = parent / new_filename
|
||||
shutil.move(path, new_path)
|
||||
|
||||
def cli_dir_flatten(args):
|
||||
|
||||
input_path = args.path.resolve()
|
||||
|
||||
logger.info("Flattening directory at: %s", input_path)
|
||||
for_each_file(input_path, [], partial(_flatten, input_path))
|
||||
|
||||
|
||||
def add_parser(subparsers):
|
||||
|
||||
parser = subparsers.add_parser("file")
|
||||
parsers = parser.add_subparsers(required=True)
|
||||
|
||||
remove_parser = parsers.add_parser("remove")
|
||||
remove_parser.add_argument(
|
||||
"--path",
|
||||
type=Path,
|
||||
help="Path to directory to remove files from",
|
||||
)
|
||||
remove_parser.add_argument(
|
||||
"--ext",
|
||||
type=str,
|
||||
help="Extension of file to remove",
|
||||
)
|
||||
remove_parser.set_defaults(func=cli_file_remove)
|
||||
|
||||
flatten_parser = parsers.add_parser("flatten")
|
||||
flatten_parser.add_argument(
|
||||
"--path",
|
||||
type=Path,
|
||||
help="Path to directory to flatten",
|
||||
)
|
||||
flatten_parser.set_defaults(func=cli_dir_flatten)
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
from pathlib import Path
|
||||
import os
|
||||
import sys
|
||||
from typing import Callable
|
||||
|
||||
def for_each_file(path: Path, extensions: list[str] | None, func: Callable):
|
||||
for eachFile in get_files_recursive(path, extensions):
|
||||
func(eachFile)
|
||||
|
||||
def get_files_recursive(path: Path, extensions: list[str] | None) -> list[Path]:
|
||||
if not extensions:
|
||||
|
|
0
src/jgutils/image/__init__.py
Normal file
0
src/jgutils/image/__init__.py
Normal file
35
src/jgutils/image/cli.py
Normal file
35
src/jgutils/image/cli.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from pathlib import Path
|
||||
import os
|
||||
|
||||
from jgutils import converters
|
||||
from jgutils.image.convert import pillow_convert
|
||||
|
||||
|
||||
def cli_image_convert(args):
|
||||
|
||||
input_path = args.input.resolve()
|
||||
if not input_path.is_dir():
|
||||
output_dir = input_path.parent
|
||||
else:
|
||||
output_dir = input_path
|
||||
|
||||
converters.convert(input_path,
|
||||
output_dir,
|
||||
"HEIC",
|
||||
"png",
|
||||
pillow_convert,
|
||||
True)
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser("image")
|
||||
parsers = parser.add_subparsers(required=True)
|
||||
convert_parser = parsers.add_parser("convert")
|
||||
|
||||
convert_parser.add_argument(
|
||||
"--input",
|
||||
type=Path,
|
||||
default=Path(os.getcwd()),
|
||||
help="Path to run the conversion from",
|
||||
)
|
||||
convert_parser.set_defaults(func=cli_image_convert)
|
||||
|
35
src/jgutils/image/convert.py
Normal file
35
src/jgutils/image/convert.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from pathlib import Path
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from PIL import Image
|
||||
from pillow_heif import HeifImagePlugin
|
||||
from PIL.ExifTags import TAGS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def pillow_convert(paths: tuple[Path]):
|
||||
input_path, output_path = paths
|
||||
logging.info("Converting %s to %s: ", input_path, output_path)
|
||||
with Image.open(input_path) as im:
|
||||
im.save(output_path, exif=im.getexif())
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
path = sys.argv[1]
|
||||
image = Image.open(path)
|
||||
print(image.info)
|
||||
print(image.getxmp())
|
||||
exifdata = image.getexif()
|
||||
print(exifdata)
|
||||
for tag_id in exifdata:
|
||||
print(tag_id)
|
||||
tag = TAGS.get(tag_id,tag_id)
|
||||
print(tag)
|
||||
data = exifdata.get(tag_id)
|
||||
print(data)
|
||||
# we decode bytes
|
||||
if isinstance(data,bytes):
|
||||
data = data.decode()
|
||||
print(f'{tag:25}: {data}')
|
|
@ -1,56 +1,23 @@
|
|||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
import jgutils.filesystem.cli
|
||||
import jgutils.image.cli
|
||||
import jgutils.music.cli
|
||||
|
||||
from jgutils import music
|
||||
from jgutils.serialization import write_model
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def cli_music_read(args):
|
||||
collection = music.collection.read(args.collection.resolve())
|
||||
write_model(collection)
|
||||
|
||||
def cli_music_refresh(args):
|
||||
music.refresh(args.collection.resolve(),
|
||||
args.work_dir.resolve())
|
||||
|
||||
def main_cli():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparsers = parser.add_subparsers(required=True)
|
||||
|
||||
music_parser = subparsers.add_parser("music")
|
||||
music_subparsers = music_parser.add_subparsers(required=True)
|
||||
|
||||
music_read_parser = music_subparsers.add_parser("read")
|
||||
music_read_parser.add_argument(
|
||||
"--collection",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory with input music files.",
|
||||
)
|
||||
music_read_parser.add_argument(
|
||||
"--output_path",
|
||||
type=Path,
|
||||
default=Path() / "music_collection.json",
|
||||
help="Path to save collection info json to.",
|
||||
)
|
||||
music_read_parser.set_defaults(func=cli_music_read)
|
||||
|
||||
music_refresh_parser = music_subparsers.add_parser("refresh")
|
||||
music_refresh_parser.add_argument(
|
||||
"--collection",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory with the music collection.",
|
||||
)
|
||||
music_refresh_parser.add_argument(
|
||||
"--work_dir",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory for intermediate storage.",
|
||||
)
|
||||
music_refresh_parser.set_defaults(func=cli_music_refresh)
|
||||
jgutils.filesystem.cli.add_parser(subparsers)
|
||||
jgutils.image.cli.add_parser(subparsers)
|
||||
jgutils.music.cli.add_parser(subparsers)
|
||||
|
||||
logging.basicConfig()
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
|
48
src/jgutils/music/cli.py
Normal file
48
src/jgutils/music/cli.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from pathlib import Path
|
||||
|
||||
from jgutils.music import collection
|
||||
from jgutils.serialization import write_model
|
||||
|
||||
|
||||
def cli_music_read(args):
|
||||
collection = collection.read(args.collection.resolve())
|
||||
write_model(collection)
|
||||
|
||||
def cli_music_refresh(args):
|
||||
collection.refresh(args.collection.resolve(),
|
||||
args.work_dir.resolve())
|
||||
|
||||
def add_parser(subparsers):
|
||||
parser = subparsers.add_parser("music")
|
||||
parsers = parser.add_subparsers(required=True)
|
||||
|
||||
read_parser = parsers.add_parser("read")
|
||||
read_parser.add_argument(
|
||||
"--collection",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory with input music files.",
|
||||
)
|
||||
read_parser.add_argument(
|
||||
"--output_path",
|
||||
type=Path,
|
||||
default=Path() / "music_collection.json",
|
||||
help="Path to save collection info json to.",
|
||||
)
|
||||
read_parser.set_defaults(func=cli_music_read)
|
||||
|
||||
refresh_parser = parsers.add_parser("refresh")
|
||||
refresh_parser.add_argument(
|
||||
"--collection",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory with the music collection.",
|
||||
)
|
||||
refresh_parser.add_argument(
|
||||
"--work_dir",
|
||||
type=Path,
|
||||
default=Path(),
|
||||
help="Directory for intermediate storage.",
|
||||
)
|
||||
refresh_parser.set_defaults(func=cli_music_refresh)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
for f in $(find . -name '*.HEIC');
|
||||
do echo "Converting $f";
|
||||
/home/jgrogan/code/ImageMagick-7.1.1-35/utilities/magick mogrify -format png -quality 100% "$f";
|
||||
done;
|
Loading…
Reference in a new issue