├── demo.jpg ├── .gitignore ├── setup.py ├── LICENSE.txt ├── README.md └── guetzli_recursively.py /demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanrax/guetzli-recursively/HEAD/demo.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | guetzli_recursively\.egg-info/ 3 | 4 | dist/ 5 | 6 | build/lib/ 7 | 8 | MANIFEST 9 | 10 | setup\.cfg 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | setup( 3 | name = 'guetzli_recursively', 4 | py_modules=['guetzli_recursively'], 5 | version = '1.3.0', 6 | description = 'Script in Python to convert all the jpeg of a folder recursively with Guetzli.', 7 | author = 'Andros Fenollosa', 8 | author_email = 'andros@fenollosa.email', 9 | url = 'https://github.com/tanrax/guetzli-recursively', 10 | keywords = ['guetzli', 'jpeg', 'recursive'], 11 | classifiers = [], 12 | install_requires=[ 13 | 'Click>=6.7', 14 | ], 15 | entry_points=''' 16 | [console_scripts] 17 | guetzli_recursively=guetzli_recursively:run 18 | ''' 19 | ) 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Guetzli is a Google program to optimize JPEG images. Unfortunately, it only works one file at a time. With this script in Python you can do it recursively a whole folder. 3 | 4 | ## Image optimized with **guetzli-recursively** 5 | 6 | ![after and before](demo.jpg) 7 | 8 | Image Author: [Senjuti Kundu](https://unsplash.com/@senjuti?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge) 9 | 10 | # Install 11 | 12 | Guetzli must be installed on your system. Follow the official instructions. 13 | [Guetzli](https://github.com/google/guetzli) 14 | 15 | and 2.7.10 or Python 3. 16 | 17 | After 18 | 19 | ```bash 20 | pip3 install guetzli-recursively 21 | ``` 22 | 23 | # Use 24 | 25 | ```bash 26 | guetzli_recursively [folder] 27 | ``` 28 | 29 | ## Example 30 | 31 | ```bash 32 | guetzli_recursively img/ 33 | ``` 34 | 35 | out 36 | 37 | ```bash 38 | img/tasks.jpg 39 | Save 6% 40 | img/portfolio/idecrea/space.jpg 41 | It is not necessary to optimize 42 | img/portfolio/home.jpg 43 | Save 3% 44 | ``` 45 | 46 | # Quality 47 | 48 | Must be greater than or equal to 84. 49 | 50 | ```bash 51 | guetzli_recursively --quality 85 img/ 52 | ``` 53 | 54 | # Mem limit (bytes) 55 | 56 | ```bash 57 | guetzli_recursively --memlimit 28000 img/ 58 | ``` 59 | 60 | # GUIs 61 | 62 | - [Mac OS](https://github.com/tanrax/guetzli-recursively-gui) 63 | -------------------------------------------------------------------------------- /guetzli_recursively.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Libraries 4 | import click 5 | from os import path, walk, remove, rename 6 | from imghdr import what 7 | from subprocess import call 8 | 9 | # Variables 10 | TEMP_FILE = 'temp.jpg' 11 | TYPES = ('jpeg',) 12 | LIMIT_QUALITY = 84 13 | 14 | 15 | @click.command() 16 | @click.option( 17 | '--quality', 18 | default=100, 19 | help='Quality >= {quality} [default 100].'.format( 20 | quality=LIMIT_QUALITY 21 | ) 22 | ) 23 | @click.option( 24 | '--memlimit', 25 | default=28000, 26 | help='Memory limit in MB. Guetzli will fail if unable to stay under the limit. Default is 28000 MB' 27 | ) 28 | @click.argument('folder', type=click.Path(exists=True)) 29 | def run(quality, memlimit, folder): 30 | for dirpath, dirnames, files in walk(folder): 31 | for name in files: 32 | url = path.join(dirpath, name) 33 | # Check type 34 | if what(url) in TYPES or quality >= LIMIT_QUALITY: 35 | # Get urls 36 | click.echo(url) 37 | url_out = path.join(folder, TEMP_FILE) 38 | # Remove temp image 39 | try: 40 | remove(url_out) 41 | except: 42 | pass 43 | # Execute guetzli 44 | args = ['guetzli', '--quality', 45 | str(quality), '--memlimit', str(memlimit), url, url_out] 46 | print(' '.join(args)) 47 | call(args) 48 | # Print your have saved 49 | size_source = path.getsize(url) 50 | try: 51 | size_out = path.getsize(url_out) 52 | except: 53 | size_out = size_source 54 | size_acurate = 100 * size_out / size_source 55 | # Check if it is cost effective to replace it 56 | if size_acurate < 100: 57 | # Remove source 58 | try: 59 | remove(url) 60 | except: 61 | pass 62 | # Move temp to source 63 | rename(url_out, url) 64 | click.echo( 65 | 'Save ' + str(round(100 - size_acurate, 2)) + '%') 66 | else: 67 | click.echo('It is not necessary to optimize') 68 | 69 | 70 | if __name__ == '__main__': 71 | run() 72 | --------------------------------------------------------------------------------