File size: 4,573 Bytes
e00b837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Hive Appier Framework
# Copyright (c) 2008-2024 Hive Solutions Lda.
#
# This file is part of Hive Appier Framework.
#
# Hive Appier Framework is free software: you can redistribute it and/or modify
# it under the terms of the Apache License as published by the Apache
# Foundation, either version 2.0 of the License, or (at your option) any
# later version.
#
# Hive Appier Framework 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
# Apache License for more details.
#
# You should have received a copy of the Apache License along with
# Hive Appier Framework. If not, see <http://www.apache.org/licenses/>.

__author__ = "João Magalhães <joamag@hive.pt>"
""" The author(s) of the module """

__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
""" The copyright for the module """

__license__ = "Apache License, Version 2.0"
""" The license for the module """

import os

from . import legacy
from . import exceptions


class Compress(object):
    def __init__(self):
        self._load_compress()

    def load_jsmin(self):
        try:
            import jsmin
        except ImportError:
            self.jsmin = None
            return
        self.jsmin = jsmin

    def type_jpeg(self):
        return "image/jpeg"

    def compress(self, file_path, modified=None, method=None):
        # retrieves the modification data from the requested file and
        # uses it to construct the complete (cache) key to be used in
        # cache try, in case there's a match returns the new file
        modified = modified or os.path.getmtime(file_path)
        key = "%s:%s" % (method, file_path)
        result = self.try_cache(key, modified)
        if result:
            return (len(result), legacy.BytesIO(result))

        # in case there's no provided method a proper not found exception
        # should be raised so that the end user is notified about the non
        # existence of such unset compressor (as expected)
        if method == None:
            raise exceptions.NotFoundError(message="Compressor is not defined")

        # in case the compress string is defined, tries to find the proper
        # compress method and in case it's not found raises an exception
        if not hasattr(self, "compress_" + method):
            raise exceptions.NotFoundError(message="Compressor '%s' not found" % method)

        # retrieves the proper compressor method for the requested compress
        # technique, this should be used in a dynamic way enforcing some
        # overhead to avoid extra issues while handling with files
        compressor = getattr(self, "compress_" + method)

        # opens the requested file and reads the complete set of information
        # from it closing the file object after the operation is complete
        file = open(file_path, "rb")
        try:
            data = file.read()
        finally:
            file.close()

        # runs the compressing operation using the target compressor and uses
        # the resulting (compressed) data as the value to be returned
        result = compressor(data)

        # constructs both the size and the file object from the resulting plain
        # string data (bytes sequence), these values are considered the result
        result_size = len(result)
        result_file = legacy.BytesIO(result)

        # flags the proper values in the cache so that they may be re-used in case
        # the flag remains the same for the key, then returns the resulting tuple
        self.flag_cache(key, modified, result)
        return (result_size, result_file)

    def compress_jpeg(self, file_path):
        if self.jinja:
            return self.compress_jpeg_pil(file_path)
        return self.compress_fallback(file_path)

    def compress_jpeg_pil(self, data, quality=80):
        input = legacy.BytesIO(data)
        output = legacy.BytesIO()
        image = self.pil.Image.open(input)
        image.save(output, format="jpeg", quality=quality, optimize=True)
        output.seek(0, os.SEEK_SET)
        data = output.read()
        return data

    def compress_js(self, data):
        if self.jsmin:
            return self.compress_js_jsmin(data)
        return self.compress_fallback(data)

    def compress_js_jsmin(self, data):
        return self.jsmin.jsmin(data)

    def compress_fallback(self, data):
        return data

    def _load_compress(self):
        self.load_jsmin()