Skip to content

Commit 6386438

Browse files
committed
Add do-effect CLI option
1 parent 841c33a commit 6386438

File tree

9 files changed

+154
-8
lines changed

9 files changed

+154
-8
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Options:
8888
Commands:
8989
auto Auto process (resize to 1920, remove GPS, add border)...
9090
border Add internal border to image file(s), not expand the size.
91+
do-effect Do special effect to image file(s).
9192
no-gps Remove GPS location info in image file(s).
9293
resize Resize image file(s).
9394
rotate Rotate image file(s).
@@ -143,6 +144,23 @@ Options:
143144
--help Show this message and exit.
144145
```
145146

147+
#### The `do-effect` sub-command CLI options:
148+
149+
```
150+
✗ batch_img do-effect --help
151+
Usage: batch_img do-effect [OPTIONS] SRC_PATH
152+
153+
Do special effect to image file(s).
154+
155+
Options:
156+
-e, --effect [blur|hdr|neon] Do special effect to image file(s): blur, hdr,
157+
neon. [default: neon]
158+
-o, --output TEXT Output dir path. If not specified, add special
159+
effect image file(s) to the same path as the
160+
input file(s). [default: ""]
161+
--help Show this message and exit.
162+
```
163+
146164
#### The `resize` sub-command CLI options:
147165

148166
```

batch_img/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ def prepare_all_files(in_path: Path, out_path: Path | str):
382382
Returns:
383383
iterable: files list generator
384384
"""
385-
if out_path != REPLACE:
385+
if out_path and out_path != REPLACE:
386386
out_path.mkdir(parents=True, exist_ok=True)
387387
# Fix Path.glob() got 2x count on Windows 10
388388
tmp = [in_path.glob(p, case_sensitive=True) for p in PATTERNS]
@@ -416,7 +416,7 @@ def multiprocess_progress_bar(func, desc: str, tasks: list) -> int:
416416
return success_cnt
417417

418418
@staticmethod
419-
def set_out_file(in_path: Path, out_path: Path, extra: str = "") -> Path:
419+
def set_out_file(in_path: Path, out_path: Path | str, extra: str = "") -> Path:
420420
"""Set the output file path
421421
422422
Args:

batch_img/do_effect.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""class DoEffect: apply OpenCV advanced image effects to image file(s).
2-
Must separate this file from effect.py to avoid HEIC image saving error due to cv2.
32
Copyright © 2025 John Liu
43
"""
54

batch_img/interface.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,36 @@ def border(src_path, border_width, border_color, output):
9191
click.secho(msg)
9292

9393

94+
@cli.command(help="Do special effect to image file(s).")
95+
@click.argument(
96+
"src_path",
97+
required=True,
98+
)
99+
@click.option(
100+
"-e",
101+
"--effect",
102+
is_flag=False,
103+
default="neon",
104+
show_default=True,
105+
type=click.Choice(["blur", "hdr", "neon"]),
106+
help="Do special effect to image file(s): blur, hdr, neon.",
107+
)
108+
@click.option(
109+
"-o",
110+
"--output",
111+
default="",
112+
show_default=True,
113+
type=str,
114+
help="Output dir path. If not specified, add special effect image file(s)"
115+
" to the same path as the input file(s).",
116+
)
117+
def do_effect(src_path, effect, output):
118+
options = {"src_path": src_path, "effect": effect, "output": output}
119+
res = Main.do_effect(options)
120+
msg = MSG_OK if res else MSG_BAD
121+
click.secho(msg)
122+
123+
94124
@cli.command(help="Remove GPS location info in image file(s).")
95125
@click.argument(
96126
"src_path",

batch_img/main.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from batch_img.border import Border
1212
from batch_img.common import Common
1313
from batch_img.const import PKG_NAME, REPLACE
14+
from batch_img.do_effect import DoEffect
1415
from batch_img.log import Log
1516
from batch_img.no_gps import NoGps
1617
from batch_img.resize import Resize
@@ -79,6 +80,32 @@ def border(options: dict) -> bool:
7980
Common.check_latest_version(PKG_NAME)
8081
return ok
8182

83+
@staticmethod
84+
def do_effect(options: dict) -> bool:
85+
"""Add a special effect to the image file(s)
86+
87+
Args:
88+
options: input options dict
89+
90+
Returns:
91+
bool: True - Success. False - Error
92+
"""
93+
Log.init_log_file()
94+
log.debug(f"{json.dumps(options, indent=2)}")
95+
in_path = Path(options["src_path"])
96+
effect = options.get("effect")
97+
if not effect:
98+
log.error(f"Bad border width: {effect=}")
99+
return False
100+
output = options.get("output")
101+
out = Path(output) if output else ""
102+
if in_path.is_file():
103+
ok, _ = DoEffect.apply_1_image((in_path, out, effect))
104+
else:
105+
ok = DoEffect.apply_all_in_dir(in_path, out, effect)
106+
Common.check_latest_version(PKG_NAME)
107+
return ok
108+
82109
@staticmethod
83110
def no_gps(options) -> bool:
84111
"""Remove GPS location info in image file(s)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requires = ["setuptools>=80.0"]
44

55
[project]
66
name = "batch_img"
7-
version = "0.2.9"
7+
version = "0.3.0"
88
description = "Batch process (resize, rotate, remove GPS, add border, set transparency, auto do all) image files (HEIC, JPG, PNG)"
99
readme = "README.md"
1010
authors = [{ name = "John Liu", email = "rim2rim@gmail.com" }]

tests/test_common.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from .helper import DotDict
1919

2020

21-
@pytest.fixture(params=[(PKG_NAME, "0.2.9"), ("", "0.2.9")])
21+
@pytest.fixture(params=[(PKG_NAME, "0.3.0"), ("", "0.3.0")])
2222
def ver_data(request):
2323
return request.param
2424

@@ -34,7 +34,7 @@ def test_get_version(ver_data):
3434
(
3535
"0.9.9",
3636
PKG_NAME,
37-
f"🔔 Update available: 0.2.9 → 0.9.9\nRun '{PKG_NAME} --update'",
37+
f"🔔 Update available: 0.3.0 → 0.9.9\nRun '{PKG_NAME} --update'",
3838
),
3939
]
4040
)
@@ -52,7 +52,7 @@ def test_check_latest_version(mock_get_latest_pypi, data_check_latest_version):
5252

5353
@pytest.fixture(
5454
params=[
55-
(PKG_NAME, 0, "0.2.8"),
55+
(PKG_NAME, 0, "0.2.9"),
5656
("bad_bogus", 1, UNKNOWN),
5757
]
5858
)

tests/test_interface.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@
99
from click.testing import CliRunner
1010

1111
from batch_img.const import MSG_BAD, MSG_OK
12-
from batch_img.interface import auto, border, no_gps, resize, rotate, transparent
12+
from batch_img.interface import (
13+
auto,
14+
border,
15+
do_effect,
16+
no_gps,
17+
resize,
18+
rotate,
19+
transparent,
20+
)
1321

1422

1523
@pytest.fixture(
@@ -73,6 +81,29 @@ def test_error_border(mock_border, data_error_border):
7381
assert result.exception
7482

7583

84+
@pytest.fixture(
85+
params=[
86+
("src_path -e blur", True, MSG_OK),
87+
("img/file --effect hdr", False, MSG_BAD),
88+
("src_path -e neon", True, MSG_OK),
89+
]
90+
)
91+
def data_effect(request):
92+
return request.param
93+
94+
95+
@patch("batch_img.main.Main.do_effect")
96+
def test_do_effect(mock_border, data_effect):
97+
_input, res, expected = data_effect
98+
mock_border.return_value = res
99+
expected += "\n"
100+
runner = CliRunner()
101+
result = runner.invoke(do_effect, args=_input.split())
102+
print(result.output)
103+
assert not result.exception
104+
assert result.output == expected
105+
106+
76107
@pytest.fixture(
77108
params=[
78109
("src_path -o out/dir", True, MSG_OK),

tests/test_main.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,47 @@ def test_border_all_in_dir(data_border_all):
118118
assert actual == expected
119119

120120

121+
@pytest.fixture(
122+
params=[
123+
(
124+
"v_1",
125+
"v_2",
126+
{
127+
"src_path": "src/file",
128+
"effect": "blur",
129+
"output": f"{dirname(__file__)}/.out/",
130+
},
131+
"v_2",
132+
),
133+
(
134+
"v_1",
135+
"v_2",
136+
{
137+
"src_path": "src/file",
138+
"effect": "neon",
139+
},
140+
"v_2",
141+
),
142+
]
143+
)
144+
def data_do_effect(request):
145+
return request.param
146+
147+
148+
@patch("batch_img.common.Common.check_latest_version")
149+
@patch("batch_img.do_effect.DoEffect.apply_all_in_dir")
150+
@patch("batch_img.do_effect.DoEffect.apply_1_image")
151+
def test_do_effect(
152+
mock_apply_1_image, mock_apply_all_in_dir, mock_check_latest_version, data_do_effect
153+
):
154+
v_1, v_2, options, expected = data_do_effect
155+
mock_apply_1_image.return_value = v_1
156+
mock_apply_all_in_dir.return_value = v_2
157+
mock_check_latest_version.return_value = "ok"
158+
actual = Main.do_effect(options)
159+
assert actual == expected
160+
161+
121162
@pytest.fixture(
122163
params=[
123164
(

0 commit comments

Comments
 (0)