diff --git a/frontends/web/app.py b/frontends/web/app.py index f7bdc06..b1a1a29 100644 --- a/frontends/web/app.py +++ b/frontends/web/app.py @@ -2136,7 +2136,7 @@ def api_tools_rotate(): output_path = tempfile.mktemp(suffix=".jpg") try: result = subprocess.run( - ["jpegtran", "-rotate", str(rotation), "-copy", "all", "-trim", + ["jpegtran", "-rotate", str(rotation), "-copy", "all", "-outfile", output_path, input_path], capture_output=True, timeout=30 ) @@ -2158,7 +2158,7 @@ def api_tools_rotate(): output_path = tempfile.mktemp(suffix=".jpg") try: result = subprocess.run( - ["jpegtran", "-flip", "horizontal", "-copy", "all", "-trim", + ["jpegtran", "-flip", "horizontal", "-copy", "all", "-outfile", output_path, input_path], capture_output=True, timeout=30 ) @@ -2180,7 +2180,7 @@ def api_tools_rotate(): output_path = tempfile.mktemp(suffix=".jpg") try: result = subprocess.run( - ["jpegtran", "-flip", "vertical", "-copy", "all", "-trim", + ["jpegtran", "-flip", "vertical", "-copy", "all", "-outfile", output_path, input_path], capture_output=True, timeout=30 ) diff --git a/src/stegasoo/cli.py b/src/stegasoo/cli.py index ef06ed5..080cf9a 100644 --- a/src/stegasoo/cli.py +++ b/src/stegasoo/cli.py @@ -241,8 +241,20 @@ def encode( with open(carrier, "rb") as f: carrier_data = f.read() - # Determine output path - output = output or f"{Path(carrier).stem}_encoded.png" + # Determine output path and format + # Default to JPEG for JPEG carriers (preserves DCT mode benefits) + carrier_ext = Path(carrier).suffix.lower() + if not output: + if carrier_ext in ('.jpg', '.jpeg'): + output = f"{Path(carrier).stem}_encoded.jpg" + else: + output = f"{Path(carrier).stem}_encoded.png" + + # Detect output format from extension + output_ext = Path(output).suffix.lower() + use_dct = output_ext in ('.jpg', '.jpeg') + + from .steganography import EMBED_MODE_DCT, EMBED_MODE_LSB try: if file_payload: @@ -253,6 +265,8 @@ def encode( carrier_image=carrier_data, passphrase=passphrase, pin=pin, + embed_mode=EMBED_MODE_DCT if use_dct else EMBED_MODE_LSB, + dct_output_format="jpeg" if use_dct else "png", ) else: # Encode message @@ -262,6 +276,8 @@ def encode( carrier_image=carrier_data, passphrase=passphrase, pin=pin, + embed_mode=EMBED_MODE_DCT if use_dct else EMBED_MODE_LSB, + dct_output_format="jpeg" if use_dct else "png", ) # Write output diff --git a/src/stegasoo/dct_steganography.py b/src/stegasoo/dct_steganography.py index 99aabe2..ea9daf4 100644 --- a/src/stegasoo/dct_steganography.py +++ b/src/stegasoo/dct_steganography.py @@ -1252,25 +1252,17 @@ def _jpegtran_rotate(image_data: bytes, rotation: int) -> bytes: output_path = tempfile.mktemp(suffix=".jpg") try: - # jpegtran -rotate 90|180|270 -copy all -perfect + # jpegtran -rotate 90|180|270 -copy all # -copy all: preserve all metadata - # -perfect: fail if there are non-transformable edge blocks (rare) + # NOTE: Don't use -trim as it drops edge blocks and destroys stego data + # NOTE: Don't use -perfect as it fails on images with non-MCU-aligned edges result = subprocess.run( - ["jpegtran", "-rotate", str(rotation), "-copy", "all", "-perfect", + ["jpegtran", "-rotate", str(rotation), "-copy", "all", "-outfile", output_path, input_path], capture_output=True, timeout=30 ) - # If -perfect fails (edge blocks), retry without it - if result.returncode != 0: - result = subprocess.run( - ["jpegtran", "-rotate", str(rotation), "-copy", "all", "-trim", - "-outfile", output_path, input_path], - capture_output=True, - timeout=30 - ) - if result.returncode != 0: raise RuntimeError(f"jpegtran failed: {result.stderr.decode()}")