add progress bar

This commit is contained in:
rmanach 2025-10-22 11:11:46 +02:00
parent 846e7cc4b4
commit 1144dae60b
6 changed files with 73 additions and 18 deletions

View File

@ -22,6 +22,7 @@ check: format lint check-type
build: check build: check
@rm -rf dist/* @rm -rf dist/*
$(PYTHON) build-deps.py
$(PYTHON) -m hatch -v build -t wheel $(PYTHON) -m hatch -v build -t wheel
install: build install: build

47
build-deps.py Normal file
View File

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

View File

@ -6,7 +6,9 @@ build-backend = "hatchling.build"
name = "optimg" name = "optimg"
dynamic = ["version"] dynamic = ["version"]
description = "Optimize JPEG and PNG files from a directory." description = "Optimize JPEG and PNG files from a directory."
dependencies = [] dependencies = [
"tqdm==4.67.1",
]
[project.scripts] [project.scripts]
optimg = "optimg.__main__:main" optimg = "optimg.__main__:main"

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
tqdm==4.67.1

View File

@ -113,7 +113,7 @@ def main():
try: try:
result = optimizer.optimize(fg) result = optimizer.optimize(fg)
except KeyboardInterrupt: except KeyboardInterrupt:
logging.info("optimizer stopped gracefully") logging.error("oops, you stopped the optimizer abrutly...")
exit(0) exit(0)
except Exception as e: except Exception as e:
logging.fatal( logging.fatal(

View File

@ -3,10 +3,12 @@ import os
import signal import signal
import subprocess import subprocess
import time import time
from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ProcessPoolExecutor, as_completed
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
from tqdm import tqdm
from .files import File, FileGroup, FileImgMimetype from .files import File, FileGroup, FileImgMimetype
__all__ = ["ImgOptimizer", "OptimizerResult"] __all__ = ["ImgOptimizer", "OptimizerResult"]
@ -33,7 +35,7 @@ def _jpeg_optim(dest_dir: str, file: File) -> tuple["File", Optional["File"]] |
try: try:
_ = subprocess.run(cmd, shell=True, check=True) _ = subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as e: 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 return None
except Exception: except Exception:
logging.error("unexpected error while running command: %s", cmd, exc_info=True) 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() p.submit(_optim, self.dest_dir, f) for f in file_group.get_files()
] ]
file_group_optim = FileGroup() file_group_optim = FileGroup()
optimized = 0 optimized = 0
for f in futures: for f in tqdm(
if (res := f.result()) and res is not None: as_completed(futures), total=len(futures), desc="Optimizing..."
match res: ):
case (orig, None): if (res := f.result()) and res is not None:
logging.debug(f"no optimization for file: {orig}") match res:
file_group_optim.add(orig) case (orig, None):
case (orig, opti): logging.debug(f"no optimization for file: {orig}")
optimized += 1 file_group_optim.add(orig)
logging.debug( case (orig, opti):
f"optimization for file: {orig} -> {(1 - (opti.size / orig.size)) * 100:.2f}%" # noqa optimized += 1
) logging.debug(
file_group_optim.add(opti) 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") logging.info(f"optimization finished in {time.perf_counter() - start:.2f}s")
return OptimizerResult(file_group, file_group_optim, optimized) return OptimizerResult(file_group, file_group_optim, optimized)