imager-client 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
imager/Imager.py ADDED
@@ -0,0 +1,238 @@
1
+ from .ImagerData import ImagerData
2
+ from .ImagerFormat import GetFormat
3
+ from .ImagerEncode import encode
4
+ from typing import Mapping, Any, List, Union
5
+ from typing_extensions import Self
6
+
7
+
8
+ class Imager:
9
+ """Imager
10
+ Горячее создание миниатюр для картинок
11
+ @see https://github.com/pkg-ru/imager
12
+
13
+ Args:
14
+ thumb (str | Mapping[str, Any], optional): устанавливаем thumb или настройки по умолчанию. Defaults to "".
15
+ """
16
+
17
+ instance = {}
18
+
19
+ def __init__(self, thumb: Union[str, Mapping[str, Any]] = "") -> Self:
20
+ self.instance = ImagerData()
21
+
22
+ if isinstance(thumb, str):
23
+ self.thumb(thumb)
24
+ else:
25
+ self.setting(thumb)
26
+
27
+ def setting(self, setting: Mapping[str, Any]) -> Self:
28
+ """Настройки
29
+
30
+ Returns:
31
+ Imager: Imager
32
+ """
33
+ for key in setting:
34
+ if key in self.instance:
35
+ self.instance[key] = setting[key]
36
+ return self
37
+
38
+ def clone(self) -> "Imager":
39
+ """Клонируем, чтобы не вносить изменения в общий экземпляр
40
+
41
+ Returns:
42
+ Imager: Imager
43
+ """
44
+ my = Imager(self.instance)
45
+ my.instance = self.instance.copy()
46
+ return my
47
+
48
+ def copy(self) -> "Imager":
49
+ """Клонируем, чтобы не вносить изменения в общий экземпляр
50
+
51
+ Returns:
52
+ Imager: Imager
53
+ """
54
+ return self.clone()
55
+
56
+ def size(self, width: int = 0, height: int = 0) -> Self:
57
+ """Изменяем размер
58
+
59
+ Returns:
60
+ self: self
61
+ """
62
+ self.width(width)
63
+ return self.height(height)
64
+
65
+ def width(self, width: int = 0) -> Self:
66
+ """Ширина
67
+
68
+ Returns:
69
+ self: self
70
+ """
71
+ self.instance["width"] = width
72
+ return self
73
+
74
+ def height(self, height: int = 0) -> Self:
75
+ """Высота
76
+
77
+ Returns:
78
+ self: self
79
+ """
80
+ self.instance["height"] = height
81
+ return self
82
+
83
+ def quality(self, quality: int) -> Self:
84
+ """Качество
85
+
86
+ Returns:
87
+ self: self
88
+ """
89
+ self.instance["quality"] = quality
90
+ return self
91
+
92
+ def crop(self, crop: bool) -> Self:
93
+ """Кроп
94
+
95
+ Returns:
96
+ self: self
97
+ """
98
+ self.instance["crop"] = crop
99
+ return self
100
+
101
+ def color(self, r: int, g: int, b: int) -> Self:
102
+ """Цвет фона
103
+
104
+ Returns:
105
+ self: self
106
+ """
107
+ self.instance["color"] = [r, g, b]
108
+ return self
109
+
110
+ def loop(self, loop: bool) -> Self:
111
+ """Зацикливание анимации
112
+
113
+ Returns:
114
+ self: self
115
+ """
116
+ self.instance["loop"] = loop
117
+ return self
118
+
119
+ def thumb(self, thumb: str) -> Self:
120
+ """Шаблон настроек на сервере Imager
121
+
122
+ Returns:
123
+ self: self
124
+ """
125
+ self.instance["thumb"] = thumb
126
+ return self
127
+
128
+ def trim(self, active: bool, rate: int = 0, color: List[List[int]] = []) -> Self:
129
+ """Обрезать по краям прозрачные пиксели/рамку/и т.д.
130
+
131
+ Returns:
132
+ self: self
133
+ """
134
+ self.trimActive(active)
135
+ self.trimRate(rate)
136
+ return self.trimColors(color)
137
+
138
+ def trimActive(self, active: bool) -> Self:
139
+ """активность фильтра: Обрезать по краям прозрачные пиксели/рамку/и т.д.
140
+
141
+ Returns:
142
+ self: self
143
+ """
144
+ self.instance["trimActive"] = active
145
+ return self
146
+
147
+ def trimRate(self, rate: int = 0) -> Self:
148
+ """огрешность при сравнении цветов: Обрезать по краям прозрачные пиксели/рамку/и т.д.
149
+
150
+ Returns:
151
+ self: self
152
+ """
153
+ self.instance["trimRate"] = rate
154
+ return self
155
+
156
+ def trimColors(self, color: List[List[int]]) -> Self:
157
+ """список цветов: Обрезать по краям прозрачные пиксели/рамку/и т.д.
158
+
159
+ Returns:
160
+ self: self
161
+ """
162
+ if len(color) > 0:
163
+ newColors = []
164
+ for item in color:
165
+ if len(item) == 3:
166
+ newColors.append(item)
167
+ else:
168
+ break
169
+ self.instance["trimColor"] = newColors
170
+ return self
171
+
172
+ def convert(
173
+ self, file: str, format: str, setting: Union[None, Mapping[str, Any]] = None
174
+ ) -> str:
175
+ """Получаем ассет на миниатюру в указаном формате
176
+
177
+ Args:
178
+ file (str): путь к исходной картинке
179
+ format (str): формат файла миниатюры
180
+ setting (None | Mapping[str, Any], optional): Настройки. Defaults to None.
181
+
182
+ Returns:
183
+ str: asset path
184
+ """
185
+ if setting != None:
186
+ instance = self.clone().setting(setting).instance
187
+ else:
188
+ instance = self.instance
189
+
190
+ file_arr = file.split(".")
191
+ lastIndex = len(file_arr) - 1
192
+ instance["format"] = file_arr[lastIndex]
193
+ if GetFormat(instance["format"]) == False:
194
+ return file
195
+
196
+ if format == "":
197
+ format = str(instance["format"]).lower()
198
+
199
+ nf = GetFormat(format)
200
+ if nf == False:
201
+ return file
202
+
203
+ instance["formatTo"] = nf
204
+ if instance["format"] == format:
205
+ # если запрашиваемый формат совпадает с текущим то не пишем в данные
206
+ instance["format"] = ""
207
+
208
+ if (
209
+ instance["format"] != ""
210
+ and instance["format"] == str(instance["format"]).lower()
211
+ ):
212
+ # если формат файла в нижнем регистре, пишем в данные только 1 байт
213
+ nf = GetFormat(instance["format"])
214
+ if nf != False:
215
+ instance["formatFrom"] = nf
216
+ instance["format"] = ""
217
+
218
+ if instance["trimActive"] != True:
219
+ instance["trimColor"] = []
220
+ instance["trimRate"] = 0
221
+
222
+ code = encode(instance)
223
+ if code == "":
224
+ return file
225
+
226
+ return ".".join(file_arr[0:lastIndex]) + "/" + code + "." + format
227
+
228
+ def get(self, file: str, setting: Union[None, Mapping[str, Any]] = None) -> str:
229
+ """Получаем ассет на миниатюру в формате исходного файла
230
+
231
+ Args:
232
+ file (str): путь к исходной картинке
233
+ setting (None | Mapping[str, Any], optional): Настройки. Defaults to None.
234
+
235
+ Returns:
236
+ str: asset path
237
+ """
238
+ return self.convert(file, "", setting)
imager/ImagerData.py ADDED
@@ -0,0 +1,46 @@
1
+ from typing import Mapping, Any, List
2
+
3
+
4
+ def ImagerData() -> Mapping[str, Any]:
5
+ return {
6
+ "trimColor": [],
7
+ "format": "",
8
+ "thumb": "",
9
+ "color": [],
10
+ "width": 0,
11
+ "height": 0,
12
+ "formatTo": 0,
13
+ "formatFrom": 0,
14
+ "quality": 0,
15
+ "trimRate": False,
16
+ "loop": True,
17
+ "trimActive": False,
18
+ "crop": False,
19
+ }
20
+
21
+
22
+ Flags: Mapping[str, int] = {
23
+ "width": 1 << 0,
24
+ "height": 1 << 1,
25
+ "quality": 1 << 2,
26
+ "format": 1 << 3,
27
+ "color": 1 << 4,
28
+ "loop": 1 << 5,
29
+ "thumb": 1 << 6,
30
+ "trimActive": 1 << 7,
31
+ "trimColor": 1 << 8,
32
+ "trimRate": 1 << 9,
33
+ "formatTo": 1 << 10,
34
+ "formatFrom": 1 << 11,
35
+ "crop": 1 << 12,
36
+ }
37
+
38
+
39
+ __flagsSorted = []
40
+
41
+ def FlagsSorted() -> List[str]:
42
+ """Отсортированные ключи"""
43
+ global __flagsSorted
44
+ if len(__flagsSorted) == 0:
45
+ __flagsSorted = list({key: Flags[key] for key in sorted(Flags)}.keys())
46
+ return __flagsSorted
imager/ImagerEncode.py ADDED
@@ -0,0 +1,58 @@
1
+ from typing import Mapping, Any
2
+ from base64 import urlsafe_b64encode
3
+ import struct
4
+ from .ImagerData import Flags, FlagsSorted
5
+
6
+
7
+ def encode(instance: Mapping[str, Any]) -> str:
8
+ if instance["thumb"] == "default":
9
+ instance["thumb"] = ""
10
+
11
+ myFlag = 0
12
+ newData = b""
13
+
14
+ for name in FlagsSorted():
15
+ flag = Flags[name] | 0
16
+
17
+ if name == "color" and len(list(instance[name])) > 0:
18
+ # [3]uint8
19
+ myFlag |= flag
20
+ newData += struct.pack(
21
+ ">BBB",
22
+ instance["color"][0],
23
+ instance["color"][1],
24
+ instance["color"][2],
25
+ )
26
+ elif name == "trimColor" and len(instance[name]) > 0:
27
+ # [][3]uint8
28
+ myFlag |= flag
29
+ newData += struct.pack(">B", len(instance["trimColor"]) * 3)
30
+ for color in instance["trimColor"]:
31
+ newData += struct.pack(
32
+ ">BBB", color[0], color[1], color[2]
33
+ )
34
+ elif name in ["loop", "trimActive", "crop"] and instance[name]:
35
+ # bool
36
+ myFlag |= flag
37
+ elif (
38
+ name in ["formatTo", "formatFrom", "quality", "trimRate"]
39
+ and instance[name] > 0
40
+ ):
41
+ # uint8
42
+ myFlag |= flag
43
+ newData += struct.pack(">B", instance[name])
44
+ elif name in ["width", "height"] and instance[name] > 0:
45
+ # uint16
46
+ myFlag |= flag
47
+ newData += struct.pack(">H", instance[name])
48
+ elif name in ["format", "thumb"] and instance[name] and instance[name] != "":
49
+ # string
50
+ myFlag |= flag
51
+ newData += struct.pack(">B", len(str(instance[name])))
52
+ newData += str(instance[name]).encode("utf-8")
53
+
54
+ return (
55
+ urlsafe_b64encode(struct.pack(">H", myFlag) + newData)
56
+ .rstrip(b"=")
57
+ .decode("ascii")
58
+ )
imager/ImagerFormat.py ADDED
@@ -0,0 +1,39 @@
1
+ from typing import Union
2
+
3
+ FORMAT_JPG = 1
4
+ FORMAT_JPEG = 2
5
+ FORMAT_GIF = 3
6
+ FORMAT_PNG = 4
7
+ FORMAT_APNG = 5
8
+ FORMAT_JPE = 6
9
+ FORMAT_JIF = 7
10
+ FORMAT_JFIF = 8
11
+ FORMAT_JFI = 9
12
+ FORMAT_WEBP = 10
13
+ FORMAT_AVIF = 11
14
+ FORMAT_HEIF = 12
15
+ FORMAT_HEIC = 13
16
+
17
+ FormatList = {
18
+ FORMAT_JPG: "jpg",
19
+ FORMAT_JPEG: "jpeg",
20
+ FORMAT_GIF: "gif",
21
+ FORMAT_PNG: "png",
22
+ FORMAT_APNG: "apng",
23
+ FORMAT_JPE: "jpe",
24
+ FORMAT_JIF: "jif",
25
+ FORMAT_JFIF: "jfif",
26
+ FORMAT_JFI: "jfi",
27
+ FORMAT_WEBP: "webp",
28
+ FORMAT_AVIF: "avif",
29
+ FORMAT_HEIF: "heif",
30
+ FORMAT_HEIC: "heic",
31
+ }
32
+
33
+
34
+ def GetFormat(format: str) -> Union[int, bool]:
35
+ format = format.lower()
36
+ for key in FormatList:
37
+ if FormatList[key] == format:
38
+ return key
39
+ return False
imager/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ from typing import Mapping, Any, Union
2
+ from .Imager import Imager
3
+ from .ImagerFormat import FormatList
4
+ from .ImagerData import Flags, FlagsSorted
5
+
6
+
7
+ def ImagerGet(file: str, setting: Union[None, Mapping[str, Any]] = None) -> str:
8
+ """Получаем ассет на миниатюру в формате исходного файла
9
+
10
+ Args:
11
+ file (str): путь к исходной картинке
12
+ setting (None | Mapping[str, Any], optional): Настройки. Defaults to None.
13
+
14
+ Returns:
15
+ str: asset path
16
+ """
17
+ img = Imager()
18
+ if setting != None:
19
+ img.setting(setting)
20
+ return img.get(file)
21
+
22
+
23
+ def ImagerConvert(
24
+ file: str, format: str, setting: Union[None, Mapping[str, Any]] = None
25
+ ) -> str:
26
+ """Получаем ассет на миниатюру в указаном формате
27
+
28
+ Args:
29
+ file (str): путь к исходной картинке
30
+ format (str): формат файла миниатюры
31
+ setting (None | Mapping[str, Any], optional): Настройки. Defaults to None.
32
+
33
+ Returns:
34
+ str: asset path
35
+ """
36
+ img = Imager()
37
+ if setting != None:
38
+ img.setting(setting)
39
+ return img.convert(file, format)