예제 #1
0
파일: core.py 프로젝트: humantech/smush.py
    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()
예제 #2
0
    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()
예제 #3
0
파일: core.py 프로젝트: larruda/smush.py
    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()
예제 #4
0
 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()
예제 #5
0
 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()
예제 #6
0
파일: core.py 프로젝트: humantech/smush.py
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
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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