def __init__(self, **kwargs): from smush.scratch import Scratch from smush.optimisers.formats.png import OptimisePNG from smush.optimisers.formats.jpg import OptimiseJPG from smush.optimisers.formats.gif import OptimiseGIF from smush.optimisers.formats.animated_gif import OptimiseAnimatedGIF self.optimisers = { "PNG": OptimisePNG(**kwargs), "JPEG": OptimiseJPG(**kwargs), "GIF": OptimiseGIF(**kwargs), "GIFGIF": OptimiseAnimatedGIF(**kwargs), } self.__files_scanned = 0 self.__start_time = time.time() self.exclude = {} for dir in kwargs.get("exclude"): if len(dir) == 0: continue self.exclude[dir] = True self.quiet = kwargs.get("quiet") self.identify_mime = kwargs.get("identify_mime") # setup tempfile for stdout and stderr self.stdout = Scratch() self.stderr = Scratch()
def __init__(self, **kwargs): from smush.scratch import Scratch from smush.optimisers.formats.png import OptimisePNG from smush.optimisers.formats.jpg import OptimiseJPG from smush.optimisers.formats.gif import OptimiseGIF from smush.optimisers.formats.animated_gif import OptimiseAnimatedGIF self.optimisers = { 'PNG': OptimisePNG(**kwargs), 'JPEG': OptimiseJPG(**kwargs), 'GIF': OptimiseGIF(**kwargs), 'GIFGIF': OptimiseAnimatedGIF(**kwargs) } self.__files_scanned = 0 self.__start_time = time.time() self.exclude = {} for dir in kwargs.get('exclude'): if len(dir) == 0: continue self.exclude[dir] = True self.quiet = kwargs.get('quiet') self.identify_mime = kwargs.get('identify_mime') # setup tempfile for stdout and stderr self.stdout = Scratch() self.stderr = Scratch()
def __init__(self, **kwargs): from smush.scratch import Scratch from smush.optimisers.formats.png import OptimisePNG from smush.optimisers.formats.jpg import OptimiseJPG #from smush.optimisers.formats.gif import OptimiseGIF #from smush.optimisers.formats.animated_gif import OptimiseAnimatedGIF self.optimisers = { 'PNG': OptimisePNG(**kwargs), 'JPEG': OptimiseJPG(**kwargs), #'GIF': OptimiseGIF(**kwargs), #'GIFGIF': OptimiseAnimatedGIF(**kwargs) } self.__files_scanned = 0 self.__start_time = time.time() self.exclude = {} for dir in kwargs.get('exclude'): if len(dir) == 0: continue self.exclude[dir] = True self.quiet = kwargs.get('quiet') self.identify_mime = kwargs.get('identify_mime') # setup tempfile for stdout and stderr self.stdout = Scratch() self.stderr = Scratch()
def __init__(self, **kwargs): # the number of times the _get_command iterator has been run self.iterations = 0 self.files_scanned = 0 self.files_optimised = 0 self.bytes_saved = 0 self.list_only = kwargs.get('list_only') self.array_optimised_file = [] self.quiet = kwargs.get('quiet') self.stdout = Scratch() self.stderr = Scratch()
class Smush: def __init__(self, **kwargs): from smush.scratch import Scratch from smush.optimisers.formats.png import OptimisePNG from smush.optimisers.formats.jpg import OptimiseJPG from smush.optimisers.formats.gif import OptimiseGIF from smush.optimisers.formats.animated_gif import OptimiseAnimatedGIF self.optimisers = { "PNG": OptimisePNG(**kwargs), "JPEG": OptimiseJPG(**kwargs), "GIF": OptimiseGIF(**kwargs), "GIFGIF": OptimiseAnimatedGIF(**kwargs), } self.__files_scanned = 0 self.__start_time = time.time() self.exclude = {} for dir in kwargs.get("exclude"): if len(dir) == 0: continue self.exclude[dir] = True self.quiet = kwargs.get("quiet") self.identify_mime = kwargs.get("identify_mime") # setup tempfile for stdout and stderr self.stdout = Scratch() self.stderr = Scratch() def __del__(self): self.stdout.destruct() self.stderr.destruct() def __smush(self, file): """ Optimises a file """ key = self.__get_image_format(file) if key in self.optimisers: logging.info("optimising file %s" % (file)) self.__files_scanned += 1 self.optimisers[key].set_input(file) self.optimisers[key].optimise() def process(self, dir, recursive): """ Iterates through the input directory optimising files """ if recursive: self.__walk(dir, self.__smush) else: if os.path.isdir(dir): dir = os.path.abspath(dir) for file in os.listdir(dir): if self.__checkExclude(file): continue if self.identify_mime: import mimetypes (type, encoding) = mimetypes.guess_type(file) if type and (type[:5] != "image"): continue self.__smush(os.path.join(dir, file)) elif os.path.isfile(dir): self.__smush(dir) def __walk(self, dir, callback): """ Walks a directory, and executes a callback on each file """ dir = os.path.abspath(dir) logging.info("walking %s" % (dir)) for file in os.listdir(dir): if self.__checkExclude(file): continue if self.identify_mime: import mimetypes (type, encoding) = mimetypes.guess_type(file) if type and (type[:5] != "image"): continue nfile = os.path.join(dir, file) callback(nfile) if os.path.isdir(nfile): self.__walk(nfile, callback) def __get_image_format(self, input): """ Returns the image format for a file. """ test_command = 'identify -format %%m "%s"' % input args = shlex.split(test_command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) if retcode != 0: if self.quiet == False: logging.warning(self.stderr.read().strip()) return False except OSError: logging.error("Error executing command %s. Error was %s" % (test_command, OSError)) sys.exit(1) except: # most likely no file matched if self.quiet == False: logging.warning("Cannot identify file.") return False return self.stdout.read().strip()[:6] def stats(self): output = [] output.append("\n%d files scanned:" % (self.__files_scanned)) arr = [] for key, optimiser in self.optimisers.iteritems(): # divide optimiser.files_optimised by 2 for each optimiser since each optimised file # gets counted twice output.append( " %d %ss optimised out of %d scanned. Saved %dkb" % (optimiser.files_optimised // 2, key, optimiser.files_scanned, optimiser.bytes_saved / 1024) ) arr.extend(optimiser.array_optimised_file) if len(arr) != 0: output.append("Modified files:") for filename in arr: output.append(" %s" % filename) output.append("Total time taken: %.2f seconds" % (time.time() - self.__start_time)) return {"output": "\n".join(output), "modified": arr} def __checkExclude(self, file): if file in self.exclude: logging.info("%s is excluded." % (file)) return True return False
class Optimiser(object): """ Super-class for optimisers """ input_placeholder = "__INPUT__" output_placeholder = "__OUTPUT__" # string to place between the basename and extension of output images output_suffix = "-opt.smush" def __init__(self, **kwargs): # the number of times the _get_command iterator has been run self.iterations = 0 self.files_scanned = 0 self.files_optimised = 0 self.bytes_saved = 0 self.list_only = kwargs.get('list_only') self.array_optimised_file = [] self.quiet = kwargs.get('quiet') self.stdout = Scratch() self.stderr = Scratch() def __del__(self): self.stdout.destruct() self.stderr.destruct() def set_input(self, input): self.iterations = 0 self.input = input def _get_command(self): """ Returns the next command to apply """ command = False if self.iterations < len(self.commands): command = self.commands[self.iterations] self.iterations += 1 return command def _get_output_file_name(self): """ Returns the input file name with Optimiser.output_suffix inserted before the extension """ temp = tempfile.mkstemp(suffix=Optimiser.output_suffix) try: output_file_name = temp[1] os.unlink(output_file_name) return output_file_name finally: os.close(temp[0]) def __replace_placeholders(self, command, input, output): """ Replaces the input and output placeholders in a string with actual parameter values """ return command.replace(Optimiser.input_placeholder, input).replace(Optimiser.output_placeholder, output) def _keep_smallest_file(self, input, output): """ Compares the sizes of two files, and discards the larger one """ input_size = os.path.getsize(input) if os.path.isfile(output): output_size = os.path.getsize(output) else: output_size = -1 # if the image was optimised (output is smaller than input), overwrite the input file with the output # file. if 0 < output_size < input_size: try: shutil.copyfile(output, input) self.files_optimised += 1 self.bytes_saved += (input_size - output_size) except IOError: logging.error("Unable to copy %s to %s: %s" % (output, input, IOError)) sys.exit(1) # delete the output file if os.path.isfile(output): os.unlink(output) def _is_acceptable_image(self, input): """ Returns whether the input image can be used by a particular optimiser. All optimisers are expected to define a variable called 'format' containing the file format as returned by 'identify -format %m' """ test_command = 'identify -format %%m "%s"' % input args = shlex.split(test_command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) except OSError: logging.error("Error executing command %s. Error was %s" % (test_command, OSError)) sys.exit(1) except: # most likely no file matched if not self.quiet: logging.warning("Cannot identify file.") return False if retcode != 0: if not self.quiet: logging.warning("Cannot identify file.") return False output = self.stdout.read().strip() return output.startswith(self.format) def optimise(self): """ Calls the 'optimise_image' method on the object. Tests the 'optimised' file size. If the generated file is larger than the original file, discard it, otherwise discard the input file. """ # make sure the input image is acceptable for this optimiser if not self._is_acceptable_image(self.input): logging.warning("%s is not a valid image for this optimiser" % (self.input)) return self.files_scanned += 1 while True: command = self._get_command() if not command: break if command.startswith('jpegoptim'): output_file_name = "/tmp/" + self.input.split("/").pop() command = self.__replace_placeholders(command, self.input, "/tmp") else: output_file_name = self._get_output_file_name() command = self.__replace_placeholders(command, self.input, output_file_name) logging.info("Executing %s" % (command)) args = shlex.split(command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) except OSError: logging.error("Error executing command %s. Error was %s" % (command, OSError)) sys.exit(1) if command.startswith('jpegoptim'): if not os.path.isfile(output_file_name): shutil.copy(self.input, output_file_name) if retcode != 0: if os.path.isfile(output_file_name): # gifsicle seems to fail by the file size? os.unlink(output_file_name) else: if not self.list_only: # compare file sizes if the command executed successfully self._keep_smallest_file(self.input, output_file_name) else: self._list_only(self.input, output_file_name) def _list_only(self, input, output): """ Always keeps input, but still compares the sizes of two files """ input_size = os.path.getsize(input) output_size = os.path.getsize(output) if (output_size > 0 and output_size < input_size): self.files_optimised += 1 self.bytes_saved += (input_size - output_size) self.array_optimised_file.append(input) # delete the output file os.unlink(output)
class Optimiser(object): """ Super-class for optimisers """ input_placeholder = "__INPUT__" output_placeholder = "__OUTPUT__" # string to place between the basename and extension of output images output_suffix = "-opt.smush" def __init__(self, **kwargs): # the number of times the _get_command iterator has been run self.iterations = 0 self.files_scanned = 0 self.files_optimised = 0 self.bytes_saved = 0 self.list_only = kwargs.get('list_only') self.array_optimised_file = [] self.quiet = kwargs.get('quiet') self.stdout = Scratch() self.stderr = Scratch() def __del__(self): self.stdout.destruct() self.stderr.destruct() def set_input(self, input): self.iterations = 0 self.input = input def _get_command(self): """ Returns the next command to apply """ command = False if self.iterations < len(self.commands): command = self.commands[self.iterations] self.iterations += 1 return command def _get_output_file_name(self): """ Returns the input file name with Optimiser.output_suffix inserted before the extension """ temp = tempfile.mkstemp(suffix=Optimiser.output_suffix) try: output_file_name = temp[1] os.unlink(output_file_name) return output_file_name finally: os.close(temp[0]) def __replace_placeholders(self, command, input, output): """ Replaces the input and output placeholders in a string with actual parameter values """ return command.replace(Optimiser.input_placeholder, input).replace(Optimiser.output_placeholder, output) def _keep_smallest_file(self, input, output): """ Compares the sizes of two files, and discards the larger one """ input_size = os.path.getsize(input) output_size = os.path.getsize(output) # if the image was optimised (output is smaller than input), overwrite the input file with the output # file. if 0 < output_size < input_size: try: shutil.copyfile(output, input) self.files_optimised += 1 self.bytes_saved += (input_size - output_size) except IOError: logging.error("Unable to copy %s to %s: %s" % (output, input, IOError)) sys.exit(1) # delete the output file os.unlink(output) def _is_acceptable_image(self, input): """ Returns whether the input image can be used by a particular optimiser. All optimisers are expected to define a variable called 'format' containing the file format as returned by 'identify -format %m' """ test_command = 'identify -format %%m "%s"' % input args = shlex.split(test_command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) except OSError: logging.error("Error executing command %s. Error was %s" % (test_command, OSError)) sys.exit(1) except: # most likely no file matched if not self.quiet: logging.warning("Cannot identify file.") return False if retcode != 0: if not self.quiet: logging.warning("Cannot identify file.") return False output = self.stdout.read().strip() return output.startswith(self.format) def optimise(self): """ Calls the 'optimise_image' method on the object. Tests the 'optimised' file size. If the generated file is larger than the original file, discard it, otherwise discard the input file. """ # make sure the input image is acceptable for this optimiser if not self._is_acceptable_image(self.input): logging.warning("%s is not a valid image for this optimiser" % (self.input)) return self.files_scanned += 1 while True: command = self._get_command() if not command: break if command.startswith('jpegoptim'): output_file_name = "/tmp/" + self.input.split("/").pop() command = self.__replace_placeholders(command, self.input, "/tmp") else: output_file_name = self._get_output_file_name() command = self.__replace_placeholders(command, self.input, output_file_name) logging.info("Executing %s" % (command)) args = shlex.split(command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) except OSError: logging.error("Error executing command %s. Error was %s" % (command, OSError)) sys.exit(1) if command.startswith('jpegoptim'): if not os.path.isfile(output_file_name): shutil.copy(self.input, output_file_name) if retcode != 0: # gifsicle seems to fail by the file size? os.unlink(output_file_name) else: if not self.list_only: # compare file sizes if the command executed successfully self._keep_smallest_file(self.input, output_file_name) else: self._list_only(self.input, output_file_name) def _list_only(self, input, output): """ Always keeps input, but still compares the sizes of two files """ input_size = os.path.getsize(input) output_size = os.path.getsize(output) if (output_size > 0 and output_size < input_size): self.files_optimised += 1 self.bytes_saved += (input_size - output_size) self.array_optimised_file.append(input) # delete the output file os.unlink(output)
class Smush(): def __init__(self, **kwargs): from smush.scratch import Scratch from smush.optimisers.formats.png import OptimisePNG from smush.optimisers.formats.jpg import OptimiseJPG from smush.optimisers.formats.gif import OptimiseGIF from smush.optimisers.formats.animated_gif import OptimiseAnimatedGIF self.optimisers = { 'PNG': OptimisePNG(**kwargs), 'JPEG': OptimiseJPG(**kwargs), 'GIF': OptimiseGIF(**kwargs), 'GIFGIF': OptimiseAnimatedGIF(**kwargs) } self.__files_scanned = 0 self.__start_time = time.time() self.exclude = {} for dir in kwargs.get('exclude'): if len(dir) == 0: continue self.exclude[dir] = True self.quiet = kwargs.get('quiet') self.identify_mime = kwargs.get('identify_mime') # setup tempfile for stdout and stderr self.stdout = Scratch() self.stderr = Scratch() def __del__(self): self.stdout.destruct() self.stderr.destruct() def __smush(self, file): """ Optimises a file """ key = self.__get_image_format(file) if key in self.optimisers: logging.info('optimising file %s' % (file)) self.__files_scanned += 1 self.optimisers[key].set_input(file) self.optimisers[key].optimise() def process(self, dir, recursive): """ Iterates through the input directory optimising files """ if recursive: self.__walk(dir, self.__smush) else: if os.path.isdir(dir): dir = os.path.abspath(dir) for file in os.listdir(dir): if self.__checkExclude(file): continue if self.identify_mime: import mimetypes (type,encoding) = mimetypes.guess_type(file) if type and (type[:5] != "image"): continue self.__smush(os.path.join(dir, file)) elif os.path.isfile(dir): self.__smush(dir) def __walk(self, dir, callback): """ Walks a directory, and executes a callback on each file """ dir = os.path.abspath(dir) logging.info('walking %s' % (dir)) for file in os.listdir(dir): if self.__checkExclude(file): continue if self.identify_mime: import mimetypes (type,encoding) = mimetypes.guess_type(file) if type and (type[:5] != "image"): continue nfile = os.path.join(dir, file) callback(nfile) if os.path.isdir(nfile): self.__walk(nfile, callback) def __get_image_format(self, input): """ Returns the image format for a file. """ test_command = 'identify -format %%m "%s"' % input args = shlex.split(test_command) try: retcode = subprocess.call(args, stdout=self.stdout.opened, stderr=self.stderr.opened) if retcode != 0: if self.quiet == False: logging.warning(self.stderr.read().strip()) return False except OSError: logging.error('Error executing command %s. Error was %s' % (test_command, OSError)) sys.exit(1) except: # most likely no file matched if self.quiet == False: logging.warning('Cannot identify file.') return False return self.stdout.read().strip()[:6] def stats(self): output = [] output.append('\n%d files scanned:' % (self.__files_scanned)) arr = [] for key, optimiser in self.optimisers.iteritems(): # divide optimiser.files_optimised by 2 for each optimiser since each optimised file # gets counted twice output.append(' %d %ss optimised out of %d scanned. Saved %dkb' % ( optimiser.files_optimised // 2, key, optimiser.files_scanned, optimiser.bytes_saved / 1024)) arr.extend(optimiser.array_optimised_file) if (len(arr) != 0): output.append('Modified files:') for filename in arr: output.append(' %s' % filename) output.append('Total time taken: %.2f seconds' % (time.time() - self.__start_time)) return {'output': "\n".join(output), 'modified': arr} def __checkExclude(self, file): if file in self.exclude: logging.info('%s is excluded.' % (file)) return True return False