diff --git a/README.md b/README.md index 99d4074..2108742 100644 --- a/README.md +++ b/README.md @@ -90,17 +90,14 @@ Dependencies: openssl Usage: ??? -## jxl-restore.py -Check whether JPEG version of .jxl exists, remove .jxl if does. -Attempt at rescuing collection where conversion got messed up. +## jxl-convert.py +Recursively convert jpgs to jxls with additional checks. -Will fix one day. Maybe. +Status: one-off -Status: ancient one-off, broken +Dependencies: libjxl -Dependencies: None - -Usage: None +Usage: `./jxl-convert.py /path/to/pics` ## invoke-magic.bat diff --git a/jxl-convert.py b/jxl-convert.py new file mode 100755 index 0000000..f994e9c --- /dev/null +++ b/jxl-convert.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# 3rd-party sw: cjxl (libjxl) + +import itertools +import subprocess +import sys +from pathlib import Path +from typing import Final + +source_dir: Final = Path(sys.argv[1]) +if len(sys.argv) > 2: + backup_dir: Final = Path(sys.argv[2]) + + +def check_source_against_convert(source_dir: Path) -> None: + for jxl in source_dir.rglob('*.jxl'): + has_original = jxl.with_suffix('.jpg').exists() or jxl.with_suffix('.JPG').exists() + if not has_original: + print(f'Source image for {jxl} has been lost') + + +def check_source_against_backup(source_dir: Path, backup_dir: Path) -> None: + pics_src = { p.relative_to(source_dir) for p in source_dir.rglob('*.JPG') } + pics_src |= { p.relative_to(source_dir) for p in source_dir.rglob('*.jpg') } + + pics_bak = { p.relative_to(backup_dir) for p in backup_dir.rglob('*.JPG') } + pics_bak |= { p.relative_to(backup_dir) for p in backup_dir.rglob('*.jpg') } + + diff = pics_src - pics_bak + if diff: + print('Source contains the following additional images:\n', '\n'.join(map(str, diff))) + diff = pics_bak - pics_src + if diff: + print('Backup contains the following additional images:\n', '\n'.join(map(str, diff))) + + +def check_successful_conversion(source_dir: Path) -> None: + pics_src = itertools.chain(source_dir.rglob('*.jpg'), source_dir.rglob('*.JPG')) + + for jpg in pics_src: + jxl = jpg.with_suffix('.jxl') + if not jxl.exists(): + print(f'Converted counterpart for {jpg} does not exist') + + +def convert(source_dir: Path) -> None: + pics_src_it = itertools.chain(source_dir.rglob('*.jpg'), source_dir.rglob('*.JPG')) + # set offset to start from image #offset (e.g. in case of a previous error) + # e.g. offset = 5 will start with converting 5th image + offset = 0 + pics_src = list(sorted(pics_src_it))[offset:] + for i, jpg in enumerate(pics_src): + jxl = jpg.with_suffix('.jxl') + cmd = ['cjxl', '-v', '--lossless_jpeg=1', '--effort=9', '--brotli_effort=11', jpg, jxl] + try: + subprocess.run(cmd, check=True, capture_output=True, encoding='utf-8') # type: ignore[arg-type] + except subprocess.CalledProcessError as ex: + print(f'ERR processing file #{i + offset} {jpg}: {ex}') + print(f'cjxl says: {ex.stderr}\n{ex.stdout}') + sys.exit(ex.returncode) + except Exception as ex: + print(f'ERR unexpected: {ex}') + sys.exit(1) + print(f'Converted #{i + offset} {jpg}') + +# check_source_against_convert(source_dir) +# check_source_against_backup(source_dir, second_dir) +convert(source_dir) +# check_successful_conversion(source_dir) diff --git a/jxl-restore.py b/jxl-restore.py deleted file mode 100755 index 5f1163e..0000000 --- a/jxl-restore.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -import os -import shutil -import subprocess -import sys -from pathlib import Path -from typing import Final - -source: Final = Path(sys.argv[1]) -dest: Final = Path(sys.argv[2]) - -def find_source_jpg(dest_name: Path) -> Path: - ext_list = ['.jpg', '.jpeg', '.JPG', '.JPEG'] - for ext in ext_list: - res = (Path(source) / relative_path / (dest_name + ext)) - if os.path.exists(dest_file_path): - return res - raise RuntimeError('No original found', dest_name) - -for dest_root, dirs, files in os.walk(dest): - for name in files: - dest_name, dest_ext = os.path.splitext(name) - if dest_ext.lower() != '.jxl': - continue - dest_file_path = (Path(dest_root) / (dest_name + '.jpg')) - print('scanning for ', dest_file_path, '\n') - if os.path.exists(dest_file_path): # both .jxl and jpg exist - print(dest_file_path, ' already exists\n') - continue - relative_path = os.path.relpath(dest_root, dest) - print('relapath ', relative_path, '\n') - src_file_path = find_source_jpg(dest_name) - shutil.copy2(src_file_path, dest_file_path) - print('restored ', dest_file_path, ' from ', src_file_path, '\n') - #dest_jxl = (Path(dest_root) / (dest_name + '.jxl')) - #os.remove(dest_jxl) - #print('deleted ', dest_jxl, '\n')