Add photo conversion

This commit is contained in:
James Grogan 2024-11-10 17:23:46 +00:00
parent c09179f5da
commit 3e7ce98755
10 changed files with 215 additions and 54 deletions

View file

@ -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"

View file

@ -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)

View 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)

View file

@ -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:

View file

35
src/jgutils/image/cli.py Normal file
View 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)

View 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}')

View file

@ -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
View 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)

View file

@ -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;