diff --git a/Makefile b/Makefile index 0c675c5..0c74154 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ check: format lint check-type build: check @rm -rf dist/* + $(PYTHON) build-deps.py $(PYTHON) -m hatch -v build -t wheel install: build diff --git a/build-deps.py b/build-deps.py new file mode 100644 index 0000000..7aa4cf8 --- /dev/null +++ b/build-deps.py @@ -0,0 +1,47 @@ +import argparse +import os +import re + +DEPS_REGEX = "dependencies = \[[^\]]*\]" +PYPROJECT_FILENAME = "pyproject.toml" + +if __name__ == "__main__": + """ + build-deps retrieves all mandatory requirements in `requirements.txt` + and copies them in `pyproject.toml` dependencies list. + """ + parser = argparse.ArgumentParser() + parser.add_argument("-d", "--dry-run", action="store_true") + args = parser.parse_args() + + # read and store requirements + requirements: list[str] = [] + with open("requirements.txt", "r") as f: + for d in f.readlines(): + d = d.replace("\n", "") + requirements.append(f'"{d}"') + + # format requirements for pryproject.toml + deps: str = "dependencies = [" + deps += "\n" if len(requirements) >= 1 else "" + for dep in requirements: + deps += f"\t{dep},\n" + deps += "]" + + # get and replace the pyproject.toml content + content: str = "" + with open(PYPROJECT_FILENAME, "r") as f: + content = f.read() + content = re.sub(DEPS_REGEX, deps, content, 1) + + # write the new content in a temp file + with open(f"{PYPROJECT_FILENAME}.tmp", "w") as f: + f.write(content) + + # if not dry run, override the original pyproject.toml + if not args.dry_run: + try: + os.rename(f"{PYPROJECT_FILENAME}.tmp", PYPROJECT_FILENAME) + except Exception as e: + print(f"error occurred while overriding pyproject file: {e}") + os.remove(f"{PYPROJECT_FILENAME}.tmp") diff --git a/pyproject.toml b/pyproject.toml index 45a2452..b6c38fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,9 @@ build-backend = "hatchling.build" name = "optimg" dynamic = ["version"] description = "Optimize JPEG and PNG files from a directory." -dependencies = [] +dependencies = [ + "tqdm==4.67.1", +] [project.scripts] optimg = "optimg.__main__:main" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c1b8f73 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +tqdm==4.67.1 \ No newline at end of file diff --git a/src/__main__.py b/src/__main__.py index dab2660..2c21291 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -113,7 +113,7 @@ def main(): try: result = optimizer.optimize(fg) except KeyboardInterrupt: - logging.info("optimizer stopped gracefully") + logging.error("oops, you stopped the optimizer abrutly...") exit(0) except Exception as e: logging.fatal( diff --git a/src/optimizer.py b/src/optimizer.py index 4b19a77..a34c925 100644 --- a/src/optimizer.py +++ b/src/optimizer.py @@ -3,10 +3,12 @@ import os import signal import subprocess import time -from concurrent.futures import ProcessPoolExecutor +from concurrent.futures import ProcessPoolExecutor, as_completed from dataclasses import dataclass from typing import Optional +from tqdm import tqdm + from .files import File, FileGroup, FileImgMimetype __all__ = ["ImgOptimizer", "OptimizerResult"] @@ -33,7 +35,7 @@ def _jpeg_optim(dest_dir: str, file: File) -> tuple["File", Optional["File"]] | try: _ = subprocess.run(cmd, shell=True, check=True) except subprocess.CalledProcessError as e: - logging.error("error while running command: %s, err: %s", cmd, e.output) + logging.error("error while running command: %s, err: %s", cmd, e.stderr or "?") return None except Exception: logging.error("unexpected error while running command: %s", cmd, exc_info=True) @@ -130,20 +132,22 @@ class ImgOptimizer: p.submit(_optim, self.dest_dir, f) for f in file_group.get_files() ] - file_group_optim = FileGroup() - optimized = 0 - for f in futures: - if (res := f.result()) and res is not None: - match res: - case (orig, None): - logging.debug(f"no optimization for file: {orig}") - file_group_optim.add(orig) - case (orig, opti): - optimized += 1 - logging.debug( - f"optimization for file: {orig} -> {(1 - (opti.size / orig.size)) * 100:.2f}%" # noqa - ) - file_group_optim.add(opti) + file_group_optim = FileGroup() + optimized = 0 + for f in tqdm( + as_completed(futures), total=len(futures), desc="Optimizing..." + ): + if (res := f.result()) and res is not None: + match res: + case (orig, None): + logging.debug(f"no optimization for file: {orig}") + file_group_optim.add(orig) + case (orig, opti): + optimized += 1 + logging.debug( + f"optimization for file: {orig} -> {(1 - (opti.size / orig.size)) * 100:.2f}%" # noqa + ) + file_group_optim.add(opti) logging.info(f"optimization finished in {time.perf_counter() - start:.2f}s") return OptimizerResult(file_group, file_group_optim, optimized)