diff --git a/img4web.py b/img4web.py new file mode 100644 index 0000000..7a15291 --- /dev/null +++ b/img4web.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python +# -*- coding: -*- + +""" + img4web.py: optimize .jpg and .png images for the web +""" + +#============================================================================== +# This Script optimizes .jpg and .png images for the web. +# +# This follows the "Yahoo Best Practices for Speeding Up Your Web Site" about +# optimize images. +# http://developer.yahoo.com/performance/rules.html#opt_images +# +# Uses the program pngcrush, the command jpegtran of the libjpeg library and +# the program gifsicle +# +# pngcrush, http://pmt.sourceforge.net/pngcrush/ +# libjpg, http://www.ijg.org/ +# gifsicle, http://www.lcdf.org/gifsicle/ +# +# In linux they are usually available in the most popular distribution +# repositories, e.g.: +# In debian, Ubuntu as these packages: +# pngcrush +# libjpeg-progs +# gifsicle +# +# In Windows pngcrush can be downloaded at +# http://sourceforge.net/projects/pmt/files/pngcrush-executables/ +# libjpeg can be downloaded (as gnuwin32) at +# http://gnuwin32.sourceforge.net/downlinks/jpeg.php +# and gifsicle can be downloaded at +# http://www.lcdf.org/gifsicle/ +# +# How it runs? +# +# By default get a list of .jpg and .png images in the working directory (where +# script runs) and process all of them one by one. Store the processed images +# in a new subdirectory named 'processed' (I know, I didn't killed myself +# worrying about the name). Also you can specify the source & destination +# directories of the images. +#============================================================================== + +#============================================================================== +# Copyright 2009 joe di castro +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +#============================================================================== + +__author__ = "joe di castro - joe@joedicastro.com" +__license__ = "GNU General Public License version 2" +__date__ = "13/06/2012" +__version__ = "0.7" + +try: + import os + import glob + import platform + import re + import sys + from argparse import ArgumentParser + from subprocess import Popen, PIPE, call +except ImportError: + # Checks the installation of the necessary python modules + print((os.linesep * 2).join(["An error found importing one module:", + str(sys.exc_info()[1]), "You need to install it", "Stopping..."])) + sys.exit(-2) + + +def arguments(): + """Defines the command line arguments for the script.""" + cur_dir = os.path.curdir + dest_dir = os.path.join(cur_dir, "processed") + main_desc = """Optimize .jpg and .png images for the web""" + + parser = ArgumentParser(description=main_desc) + parser.add_argument("-s", "--src", dest="src", default=cur_dir, help="the " + "source path. Current dir if none is provided") + parser.add_argument("-d", "--dst", dest="dst", default=dest_dir, + help="the destination path. './processed/' if none is " + "provided") + parser.add_argument("--exif", dest="exif", action="store_true", + help="preserve the EXIF data from jpeg files") + parser.add_argument("--delete", dest="delete", action="store_true", + help="delete the original image files") + parser.add_argument("-v", "--version", action="version", + version="%(prog)s {0}".format(__version__), + help="show program's version number and exit") + return parser + + +def best_unit_size(bytes_size): + """Get a size in bytes & convert it to the best IEC prefix for readability. + + Return a dictionary with three pair of keys/values: + + 's' -- (float) Size of path converted to the best unit for easy read + 'u' -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80)) + + """ + for exp in range(0, 90, 10): + bu_size = abs(bytes_size) / pow(2.0, exp) + if int(bu_size) < 2 ** 10: + unit = {0: 'bytes', 10: 'KiB', 20: 'MiB', 30: 'GiB', 40: 'TiB', + 50: 'PiB', 60: 'EiB', 70: 'ZiB', 80: 'YiB'}[exp] + break + return {'s': bu_size, 'u': unit} + + +def get_size(the_path): + """Get size of a directory tree or a file in bytes.""" + path_size = 0 + for path, directories, files in os.walk(the_path): + for filename in files: + path_size += os.lstat(os.path.join(path, filename)).st_size + for directory in directories: + path_size += os.lstat(os.path.join(path, directory)).st_size + path_size += os.path.getsize(the_path) + return path_size + + +def check_execs_posix_win(progs): + """Check if the program is installed. + + Returns one dictionary with 1+n pair of key/values: + + A fixed key/value: + + "WinOS" -- (boolean) True it's a Windows OS, False it's a *nix OS + + for each program in progs a key/value like this: + + "program" -- (str or boolean) The Windows executable path if founded else + '' if it's Windows OS. If it's a *NIX OS + True if founded else False + + """ + execs = {'WinOS': True if platform.system() == 'Windows' else False} + # get all the drive unit letters if the OS is Windows + windows_drives = re.findall(r'(\w:)\\', + Popen('fsutil fsinfo drives', stdout=PIPE). + communicate()[0]) if execs['WinOS'] else None + + progs = [progs] if isinstance(progs, str) else progs + for prog in progs: + if execs['WinOS']: + # Set all commands to search the executable in all drives + win_cmds = ['dir /B /S {0}\*{1}.exe'.format(letter, prog) for + letter in windows_drives] + # Get the first location (usually C:) where the executable exists + for cmd in win_cmds: + execs[prog] = (Popen(cmd, stdout=PIPE, stderr=PIPE, shell=1). + communicate()[0].split(os.linesep)[0]) + if execs[prog]: + break + else: + try: + Popen([prog, '--help'], stdout=PIPE, stderr=PIPE) + execs[prog] = True + except OSError: + execs[prog] = False + return execs + + +def main(): + """Main section.""" + args = arguments().parse_args() + + # Check if exists the subdirectory for store the results, else create it + src_path = os.path.abspath(args.src) + dst_path = os.path.abspath(args.dst) + if not os.path.exists(dst_path): + os.mkdir(dst_path) + + # Get the list of all .png, .jpg and .gif images in the current folder by + # type + os.chdir(src_path) + jpg = glob.glob('*.jp[e|g]*') + png = glob.glob('*.png') + gif = glob.glob('*.gif') + + # Get the original size of the images in bytes by type + org_jpg_sz = sum((get_size(orig_jpg) for orig_jpg in jpg)) + org_png_sz = sum((get_size(orig_png) for orig_png in png)) + org_gif_sz = sum((get_size(orig_gif) for orig_gif in gif)) + + # Get the executable's names (and path for windows) of the needed programs + jpegtran = EXECS['jpegtran'] if EXECS['WinOS'] else 'jpegtran' + pngcrush = EXECS['pngcrush'] if EXECS['WinOS'] else 'pngcrush' + gifsicle = EXECS['gifsicle'] if EXECS['WinOS'] else 'gifsicle' + exif = 'all' if args.exif else 'none' + + # Process all .jpg images + for jpg_img in jpg: + call([jpegtran, '-copy', exif, '-optimize', '-perfect', '-outfile', + os.path.join(dst_path, jpg_img), + os.path.join(src_path, jpg_img)]) + + # Process all .png images + for png_img in png: + call([pngcrush, '-rem', 'alla', '-reduce', '-brute', + os.path.join(src_path, png_img), + os.path.join(dst_path, png_img)]) + + # Process all .gif images (only optimize animated ones) + for gif_img in gif: + call([gifsicle, '-O2', os.path.join(src_path, gif_img), "--output", + os.path.join(dst_path, gif_img)]) + + # Get the size of the processed images in bytes by type + os.chdir(dst_path) + prc_jpg = [j for j in glob.glob('*.jp[e|g]*') if j in jpg] + prc_png = [p for p in glob.glob('*.png') if p in png] + prc_gif = [g for g in glob.glob('*.gif') if g in gif] + prc_jpg_sz = sum((get_size(new_j) for new_j in prc_jpg)) + prc_png_sz = sum((get_size(new_p) for new_p in prc_png)) + prc_gif_sz = sum((get_size(new_g) for new_g in prc_gif)) + + # Get a human readable size + ojs = best_unit_size(org_jpg_sz) + ops = best_unit_size(org_png_sz) + ogs = best_unit_size(org_gif_sz) + + pjs = best_unit_size(prc_jpg_sz) + pps = best_unit_size(prc_png_sz) + pgs = best_unit_size(prc_gif_sz) + + tot_org = best_unit_size(org_jpg_sz + org_png_sz + org_gif_sz) + tot_prc = best_unit_size(prc_jpg_sz + prc_png_sz + prc_gif_sz) + + sjs = best_unit_size(org_jpg_sz - prc_jpg_sz) + sps = best_unit_size(org_png_sz - prc_png_sz) + sgs = best_unit_size(org_gif_sz - prc_gif_sz) + tts = best_unit_size((org_jpg_sz + org_png_sz + org_gif_sz) - + (prc_jpg_sz + prc_png_sz + prc_gif_sz)) + + # Delete original image files if requested + if args.delete: + for to_trash_jpg in jpg: + os.remove(os.path.join(src_path, to_trash_jpg)) + for to_trash_png in png: + os.remove(os.path.join(src_path, to_trash_png)) + for to_trash_gif in gif: + os.remove(os.path.join(src_path, to_trash_gif)) + + # print a little report + print('{0}{1}{0}{2:^80}{0}{1}'.format(os.linesep, '=' * 80, 'Summary')) + print(' Original Processed Save' + os.linesep) + print('.jpgs: ({6:3}){0:>6.2f} {1:8}({7:3}){2:>6.2f} {3:8}{4:>6.2f} {5}'. + format(ojs['s'], ojs['u'], pjs['s'], pjs['u'], sjs['s'], sjs['u'], + len(jpg), len(prc_jpg))) + print('.pngs: ({6:3}){0:>6.2f} {1:8}({7:3}){2:>6.2f} {3:8}{4:>6.2f} {5}'. + format(ops['s'], ops['u'], pps['s'], pps['u'], sps['s'], sps['u'], + len(png), len(prc_png))) + print('.gifs: ({6:3}){0:>6.2f} {1:8}({7:3}){2:>6.2f} {3:8}{4:>6.2f} {5}'. + format(ogs['s'], ogs['u'], pgs['s'], pgs['u'], sgs['s'], sgs['u'], + len(gif), len(prc_gif))) + print('-' * 80) + print('Total: ({6:3}){0:>6.2f} {1:8}({7:3}){2:>6.2f} {3:8}{4:>6.2f} {5}'. + format(tot_org['s'], tot_org['u'], tot_prc['s'], tot_prc['u'], + tts['s'], tts['u'], + (len(jpg) + len(png) + len(gif)), + (len(prc_jpg) + len(prc_png) + len(prc_gif)))) + +if __name__ == "__main__": + EXECS = check_execs_posix_win(['jpegtran', 'pngcrush', 'gifsicle']) + main() diff --git a/index.html b/index.html index cf9f191..dc9f75d 100644 --- a/index.html +++ b/index.html @@ -8,15 +8,15 @@ - + - + - + diff --git a/processed/1611787310332.png b/processed/1611787310332.png new file mode 100644 index 0000000..f04d8b5 Binary files /dev/null and b/processed/1611787310332.png differ diff --git a/processed/1612516958284.jpg b/processed/1612516958284.jpg new file mode 100644 index 0000000..2b6096c Binary files /dev/null and b/processed/1612516958284.jpg differ diff --git a/processed/1616297771952.jpg b/processed/1616297771952.jpg new file mode 100644 index 0000000..a5cea56 Binary files /dev/null and b/processed/1616297771952.jpg differ diff --git a/processed/1616760428581.png b/processed/1616760428581.png new file mode 100644 index 0000000..8583387 Binary files /dev/null and b/processed/1616760428581.png differ diff --git a/processed/1616760463346.jpg b/processed/1616760463346.jpg new file mode 100644 index 0000000..bff016f Binary files /dev/null and b/processed/1616760463346.jpg differ diff --git a/processed/1617309778519.jpg b/processed/1617309778519.jpg new file mode 100644 index 0000000..1d9ede4 Binary files /dev/null and b/processed/1617309778519.jpg differ diff --git a/processed/2_computers.gif b/processed/2_computers.gif new file mode 100644 index 0000000..37c9b97 Binary files /dev/null and b/processed/2_computers.gif differ diff --git a/processed/2_computers_text.gif b/processed/2_computers_text.gif new file mode 100644 index 0000000..c0f8351 Binary files /dev/null and b/processed/2_computers_text.gif differ diff --git a/processed/Sleep_2.gif b/processed/Sleep_2.gif new file mode 100644 index 0000000..faee521 Binary files /dev/null and b/processed/Sleep_2.gif differ diff --git a/processed/bsg_timeline.jpg b/processed/bsg_timeline.jpg new file mode 100644 index 0000000..7d6c710 Binary files /dev/null and b/processed/bsg_timeline.jpg differ diff --git a/processed/divisore.gif b/processed/divisore.gif new file mode 100644 index 0000000..9e6eeca Binary files /dev/null and b/processed/divisore.gif differ diff --git a/processed/fade.png b/processed/fade.png new file mode 100644 index 0000000..b01ef64 Binary files /dev/null and b/processed/fade.png differ diff --git a/processed/friends_door.gif b/processed/friends_door.gif new file mode 100644 index 0000000..11605d8 Binary files /dev/null and b/processed/friends_door.gif differ diff --git a/processed/fruit_flies.gif b/processed/fruit_flies.gif new file mode 100644 index 0000000..8c8d93a Binary files /dev/null and b/processed/fruit_flies.gif differ diff --git a/processed/fruit_flies.jpg b/processed/fruit_flies.jpg new file mode 100644 index 0000000..9327ddb Binary files /dev/null and b/processed/fruit_flies.jpg differ diff --git a/processed/fruit_flies_kaleidoscope.jpg b/processed/fruit_flies_kaleidoscope.jpg new file mode 100644 index 0000000..6c3adf7 Binary files /dev/null and b/processed/fruit_flies_kaleidoscope.jpg differ diff --git a/processed/hallofshame.gif b/processed/hallofshame.gif new file mode 100644 index 0000000..20136bc Binary files /dev/null and b/processed/hallofshame.gif differ diff --git a/processed/lain.gif b/processed/lain.gif new file mode 100644 index 0000000..60c6ee4 Binary files /dev/null and b/processed/lain.gif differ diff --git a/processed/lain_nonoc.gif b/processed/lain_nonoc.gif new file mode 100644 index 0000000..79aeb09 Binary files /dev/null and b/processed/lain_nonoc.gif differ diff --git a/processed/landing.png b/processed/landing.png new file mode 100644 index 0000000..8583387 Binary files /dev/null and b/processed/landing.png differ diff --git a/processed/landing_cropped.png b/processed/landing_cropped.png new file mode 100644 index 0000000..8c45b3e Binary files /dev/null and b/processed/landing_cropped.png differ diff --git a/processed/lezzo.org.gif b/processed/lezzo.org.gif new file mode 100644 index 0000000..12e3992 Binary files /dev/null and b/processed/lezzo.org.gif differ diff --git a/processed/lezzo.org.png b/processed/lezzo.org.png new file mode 100644 index 0000000..37b98e5 Binary files /dev/null and b/processed/lezzo.org.png differ diff --git a/processed/lezzo.org2.gif b/processed/lezzo.org2.gif new file mode 100644 index 0000000..cfc3b72 Binary files /dev/null and b/processed/lezzo.org2.gif differ diff --git a/processed/logolezzo.gif b/processed/logolezzo.gif new file mode 100644 index 0000000..ea37e4c Binary files /dev/null and b/processed/logolezzo.gif differ diff --git a/processed/media-server-bg.jpg b/processed/media-server-bg.jpg new file mode 100644 index 0000000..ee3de24 Binary files /dev/null and b/processed/media-server-bg.jpg differ diff --git a/processed/pines.png b/processed/pines.png new file mode 100644 index 0000000..55b8f9b Binary files /dev/null and b/processed/pines.png differ diff --git a/processed/space_background.gif b/processed/space_background.gif new file mode 100644 index 0000000..db7f721 Binary files /dev/null and b/processed/space_background.gif differ diff --git a/processed/walloffame.gif b/processed/walloffame.gif new file mode 100644 index 0000000..1a0bd5f Binary files /dev/null and b/processed/walloffame.gif differ diff --git a/processed/yotsuba.gif b/processed/yotsuba.gif new file mode 100644 index 0000000..f39ee93 Binary files /dev/null and b/processed/yotsuba.gif differ