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

1#!/usr/bin/env python3 

2 

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. 

17 

18 chunksize : int, optional 

19 the size of the chunks of any files which are read in (in bytes) 

20 """ 

21 

22 # Import standard modules ... 

23 import os 

24 import shutil 

25 import subprocess 

26 import tempfile 

27 

28 # Import sub-functions ... 

29 from ..sha512 import sha512 

30 

31 # ************************************************************************** 

32 

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" 

37 

38 # Check that the image exists ... 

39 if not os.path.exists(fname1): 

40 raise Exception(f"\"{fname1}\" does not exist") from None 

41 

42 # Create temporary directory ... 

43 with tempfile.TemporaryDirectory(prefix = "jpegtran.") as tname: 

44 # Create temporary name ... 

45 fname2 = f"{tname}/image.jpg" 

46 

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 ) 

63 

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 

70 

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 

77 

78 # Replace the original ... 

79 shutil.move(fname2, fname1)