Coverage for pyguymer3/image/jpegtran.py: 4%
23 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def jpegtran(
5 fname1,
6 /,
7 *,
8 chunksize = 1048576,
9 debug = __debug__,
10 jpegtranPath = None,
11 timeout = 60.0,
12):
13 """
14 "jpegtran" does not modify, but it does touch, the image even if it cannot
15 make it smaller, therefore it is NOT safe to keep on running "jpegtran" on
16 the same JPG over and over again.
18 chunksize : int, optional
19 the size of the chunks of any files which are read in (in bytes)
20 """
22 # Import standard modules ...
23 import os
24 import shutil
25 import subprocess
26 import tempfile
28 # Import sub-functions ...
29 from ..sha512 import sha512
31 # **************************************************************************
33 # Try to find the paths if the user did not provide them ...
34 if jpegtranPath is None:
35 jpegtranPath = shutil.which("jpegtran")
36 assert jpegtranPath is not None, "\"jpegtran\" is not installed"
38 # Check that the image exists ...
39 if not os.path.exists(fname1):
40 raise Exception(f"\"{fname1}\" does not exist") from None
42 # Create temporary directory ...
43 with tempfile.TemporaryDirectory(prefix = "jpegtran.") as tname:
44 # Create temporary name ...
45 fname2 = f"{tname}/image.jpg"
47 # Optimise JP[E]G ...
48 subprocess.run(
49 [
50 jpegtranPath,
51 "-copy", "all",
52 "-optimise",
53 "-outfile", fname2,
54 "-perfect",
55 fname1,
56 ],
57 check = True,
58 encoding = "utf-8",
59 stderr = subprocess.DEVNULL,
60 stdout = subprocess.DEVNULL,
61 timeout = timeout,
62 )
64 # Find the two sizes and don't replace the original if the new one is
65 # larger, or equal ...
66 if os.path.getsize(fname2) >= os.path.getsize(fname1):
67 if debug:
68 print(f"INFO: Skipping because \"{fname2}\" is larger than, or equal to, \"{fname1}\"")
69 return
71 # Find the two hashes and don't replace the original if the new one is
72 # the same ...
73 if sha512(fname1, chunksize = chunksize) == sha512(fname2, chunksize = chunksize):
74 if debug:
75 print(f"INFO: Skipping because \"{fname2}\" is the same as \"{fname1}\"")
76 return
78 # Replace the original ...
79 shutil.move(fname2, fname1)