Last active
January 2, 2016 03:29
-
-
Save EdgeCaseBerg/8244172 to your computer and use it in GitHub Desktop.
Finding Image References in source trees Recursive search for image filenames within a src tree Example Use: python image-ref.py -i /home/user/pictures -s /home/user/website/httpdocs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| # -*- coding: iso-8859-15 -*- | |
| """ | |
| Script to find image references | |
| Authors | |
| ------------------------------- | |
| - Ethan J. Eldridge [01/2014] | |
| Looks through images directory to find list of files | |
| and then searches the src tree for references to those | |
| images. | |
| Output's a list of images to stdout for piping purposes. | |
| Output is in the format: | |
| FILENAME PARENT_DIR WIDTHxHEIGHT [REF 1, REF 2, ...] | |
| Options: | |
| -h, --help | |
| Lists basic help. | |
| -i, --img-path= [default = ../web-app/images ] | |
| Specify the directory to find images in. The image directory must only contain images | |
| -s, --src-tree= [default = ../] | |
| Specifies the directory to try to find image references in | |
| -d, --defaults | |
| Use defaults and override other options | |
| --absolute [default = off] | |
| Instead of using current working directory, the paths from img-path and src-tree | |
| are assuming to be absolute. | |
| --no-base [default = off, -d sets True ] | |
| Makes all output for src-tree and images be relative to their base paths. | |
| Is off unless -d is used or the switch is given | |
| --ext-ignore= | |
| Enter a list of extensions to ignore, seperated by commas, with no whitespace | |
| inbetween each extension. The dot's at the beginning of an extension are optional | |
| """ | |
| def usage(): | |
| print """ | |
| images.py [OPTIONS] | |
| -h, --help print this help | |
| -i, --img-path=<Image directory to traverse> | |
| -s, --src-tree=<Source Tree to find references in> | |
| -d, --defaults Use default directory paths used in example below | |
| --absolute Use absolute directory paths, defaults to False | |
| --no-base Remove current working directory from path names in references | |
| --ext-ignore=<csv list of extensions to ignore in src-tree> | |
| Examples: | |
| python images.py -i ../web-app/images -s ../ | |
| ./images.py --absolute -i /home/user/images -s /home/user/program | |
| ./images.py -d --no-base | |
| """ | |
| import os, sys, getopt, sets | |
| try: | |
| opts, args = getopt.getopt(sys.argv[1:], "dhi:s:", ["help", "img-path=", "src-tree=", "absolute", "defaults", "no-base", "ext-ignore="]) | |
| except getopt.GetoptError: | |
| usage() | |
| sys.exit(2) | |
| #Defaults: | |
| images_path = "../web-app/images" | |
| src_tree_path = "../" | |
| base_path = os.getcwd() | |
| remove_base = False | |
| ignored_extensions = [".war", ".gzip", ".sql", ".pdf"] | |
| #If you want additional rules for files, then place the beginnings of the image name here | |
| special_names = ["fb", "facebook","bebo","myspace","google","instagram","twitter"] | |
| for opt, arg in opts: | |
| if opt in ("-h", "--help"): | |
| usage() | |
| sys.exit() | |
| elif opt in ("-i", "--img-path"): | |
| images_path = arg | |
| elif opt in ("-s", "--src-tree"): | |
| src_tree_path = arg | |
| elif opt in ("--absolute"): | |
| base_path = "/" | |
| elif opt in ("--no-base"): | |
| remove_base = True | |
| elif opt in ("-d", "--defaults"): | |
| remove_base = True | |
| images_path = "../web-app/images" | |
| src_tree_path = "../" | |
| base_path = os.getcwd() | |
| elif opt in ("--ext-ignore"): | |
| ie = arg.split(",") | |
| new_ignores = [] | |
| for possible_ext in ie: | |
| if possible_ext[0] == ".": | |
| new_ignores.append(possible_ext) | |
| else: | |
| new_ignores.append(".%s" % possible_ext) | |
| ignored_extensions = new_ignores | |
| images_path = os.path.normpath( os.path.join(base_path, images_path) ) | |
| src_tree_path = os.path.normpath( os.path.join(base_path, src_tree_path) ) | |
| deprecated_image_path = os.path.join(images_path, "deprecated") | |
| if not os.path.exists(images_path): | |
| print "Image Path does not exist!" | |
| if not os.path.exists(src_tree_path): | |
| print "Src Tree Path does not exist!" | |
| #Mind Edge case | |
| if len(opts) == 0: | |
| usage() | |
| sys.exit() | |
| #Thank you: http://stackoverflow.com/questions/15800704/python-get-image-size-without-loading-image-into-memory | |
| #------------------------------------------------------------------------------- | |
| # Name: get_image_size | |
| # Purpose: extract image dimensions given a file path using just | |
| # core modules | |
| # | |
| # Author: Paulo Scardine (based on code from Emmanuel VAÏSSE), | |
| # Ethan Eldridge (ICO) | |
| # Created: 26/09/2013 | |
| # Copyright: (c) Paulo Scardine 2013 | |
| # Licence: MIT | |
| #------------------------------------------------------------------------------- | |
| import struct | |
| class UnknownImageFormat(Exception): | |
| pass | |
| def get_image_size(file_path): | |
| """ | |
| Return (width, height) for a given img file content - no external | |
| dependencies except the os and struct modules from core | |
| """ | |
| size = os.path.getsize(file_path) | |
| with open(file_path) as input: | |
| height = -1 | |
| width = -1 | |
| data = input.read(25) | |
| if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'): | |
| # GIFs | |
| w, h = struct.unpack("<HH", data[6:10]) | |
| width = int(w) | |
| height = int(h) | |
| elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n') | |
| and (data[12:16] == 'IHDR')): | |
| # PNGs | |
| w, h = struct.unpack(">LL", data[16:24]) | |
| width = int(w) | |
| height = int(h) | |
| elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'): | |
| # older PNGs? | |
| w, h = struct.unpack(">LL", data[8:16]) | |
| width = int(w) | |
| height = int(h) | |
| elif (size >= 2) and data.startswith('\377\330'): | |
| # JPEG | |
| msg = " raised while trying to decode as JPEG." | |
| input.seek(0) | |
| input.read(2) | |
| b = input.read(1) | |
| try: | |
| while (b and ord(b) != 0xDA): | |
| while (ord(b) != 0xFF): b = input.read(1) | |
| while (ord(b) == 0xFF): b = input.read(1) | |
| if (ord(b) >= 0xC0 and ord(b) <= 0xC3): | |
| input.read(3) | |
| h, w = struct.unpack(">HH", input.read(4)) | |
| break | |
| else: | |
| input.read(int(struct.unpack(">H", input.read(2))[0])-2) | |
| b = input.read(1) | |
| width = int(w) | |
| height = int(h) | |
| except struct.error: | |
| raise UnknownImageFormat("StructError" + msg) | |
| except ValueError: | |
| raise UnknownImageFormat("ValueError" + msg) | |
| except Exception as e: | |
| raise UnknownImageFormat(e.__class__.__name__ + msg) | |
| elif (file_path.endswith('.ico')): | |
| #Adding ICO handling - EJE | |
| #see http://en.wikipedia.org/wiki/ICO_(file_format) | |
| input.seek(0) | |
| reserved = input.read(2) | |
| if 0 != struct.unpack("<H", reserved )[0]: | |
| raise UnknownImageFormat("Corrupt ICON File") | |
| format = input.read(2) | |
| assert 1 == struct.unpack("<H", format)[0] | |
| num = input.read(2) | |
| num = struct.unpack("<H", num)[0] | |
| if num > 1: | |
| import warnings | |
| warnings.warn("ICO File contains more than one image") | |
| #http://msdn.microsoft.com/en-us/library/ms997538.aspx | |
| w = input.read(1) | |
| h = input.read(1) | |
| width = ord(w) | |
| height = ord(h) | |
| else: | |
| raise UnknownImageFormat( | |
| "Sorry, don't know how to get information from %s." % file_path | |
| ) | |
| return width, height | |
| def remove_src_path(paths): | |
| if remove_base: | |
| nr = set() | |
| for ref in paths: | |
| nr.add( ref.replace(src_tree_path, "") ) | |
| return nr | |
| return paths | |
| def remove_img_path(path): | |
| if remove_base: | |
| path = path.replace(images_path, "") | |
| if path == "": | |
| path = "/" | |
| return path | |
| class ImageEntry: | |
| def __init__(self, name, location): | |
| self.name = name | |
| self.parent = location | |
| self.references = set() | |
| def setSize(self, dimensions): | |
| self.width = dimensions[0] | |
| self.height = dimensions[1] | |
| def addReference(self, ref): | |
| self.references.add(ref) | |
| def __str__(self): | |
| references = remove_src_path(self.references) | |
| location = remove_img_path(self.parent) | |
| return "Name: %s, Parent: %s, Dims: %dx%d References: [%s]" % (self.name, location, self.width, self.height, ",".join(references) ) | |
| def __repr__(self): | |
| references = remove_src_path(self.references) | |
| location = remove_img_path(self.parent) | |
| return "%s %s %dx%d [%s]" % (self.name, location, self.width, self.height, ",".join(references)) | |
| def rec_findImgs(path, imgs=set()): | |
| """ | |
| Fill the imgs array with information about the images | |
| found within the path. (Recurses directories) | |
| """ | |
| dirs = os.listdir( path ) | |
| for f in dirs: | |
| if os.path.isfile(os.path.join(path, f)): | |
| img = ImageEntry(f, path) | |
| img.setSize( get_image_size(os.path.join(path, f)) ) | |
| imgs.add( img ) | |
| if os.path.isdir(os.path.join(path, f)): | |
| if not f.startswith('.') and not os.path.join(path,f).__contains__(deprecated_image_path): #ignore hidden and deprecated | |
| imgs = rec_findImgs(os.path.join(path, f), imgs) | |
| return imgs | |
| images = rec_findImgs(images_path, set()) | |
| def in_ignored_extensions(file_path): | |
| ext = os.path.splitext(file_path)[1] | |
| return ext in ignored_extensions | |
| import re | |
| def rec_find_refs(path, search_expressions, refs=set()): | |
| """ | |
| Find references to the search string within the path (recursive) | |
| """ | |
| dirs = os.listdir( path ) | |
| for f in dirs: | |
| if os.path.isfile(os.path.join(path, f)): | |
| if not in_ignored_extensions(os.path.join(path,f)): | |
| with open(os.path.join(path,f), "r" ) as tbsearched: | |
| line_no = 0 | |
| for line in tbsearched: | |
| line_no += 1 | |
| for regex in search_expressions: | |
| if regex.findall(line, re.IGNORECASE): | |
| refs.add("%s Line %d" %(os.path.join(path,f), line_no)) | |
| if os.path.isdir(os.path.join(path, f)): | |
| if not f.startswith('.'): #ignore hidden | |
| refs = rec_find_refs(os.path.join(path, f), search_expressions, refs) | |
| return refs | |
| #Make Unused Images directory if it doesn't not exists. | |
| #Create at base of images directory | |
| if os.path.exists(deprecated_image_path): | |
| print "Using directory %s for deprecated images" % deprecated_image_path | |
| else: | |
| try: | |
| os.mkdir(deprecated_image_path) | |
| print "Using directory %s for deprecated images" % deprecated_image_path | |
| except OSError,err: | |
| print "Couldn't make deprecated directory %s" % err.strerror | |
| sys.exit(1) | |
| import errno | |
| def get_mirrored_dirs_name(base, path ): | |
| #Replace base with deprecated base | |
| dirs_to_make = path.replace(base, deprecated_image_path) | |
| return dirs_to_make | |
| possible_dyn = re.compile("(%s)" % "|".join(['^{0}'.format(nn) for nn in special_names]) , re.IGNORECASE) | |
| for img in images: | |
| search_expressions = [] | |
| search_expressions.append( re.compile("%s" % img.name) ) | |
| #If The image seems to be a special name then add | |
| #In a search for /images/$ followed by network somewhere | |
| if possible_dyn.match(img.name): | |
| search_expressions.append( re.compile("images\/\$.*network|platform", re.IGNORECASE ) ) | |
| img.references = rec_find_refs(src_tree_path, search_expressions, set()) | |
| if len(img.references) == 0: | |
| #Move the unreferenced image to the deprecated folder | |
| #if it doesn't exist there already | |
| mir_dir = get_mirrored_dirs_name(images_path, img.parent) | |
| if not os.path.exists(mir_dir): | |
| try: | |
| os.makedirs(mir_dir) | |
| except OSError, err: | |
| if err.errno == errno.EEXIST and os.path.isdir(path): | |
| pass | |
| else: raise | |
| if not os.path.exists(os.path.join(mir_dir, img.name)): | |
| os.rename(os.path.join(img.parent, img.name), os.path.join(mir_dir, img.name)) | |
| else: | |
| print img.__repr__() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment