]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/util.py
Cache generated thumbnails
[ipfire.org.git] / src / backend / util.py
CommitLineData
9523790a 1#!/usr/bin/python3
66862195 2
5ef115cd
MT
3import PIL.Image
4import PIL.ImageFilter
5import io
6import logging
e96e445b 7import random
9523790a 8import re
e96e445b 9import string
75d9b3da 10import unicodedata
e96e445b 11
9523790a
MT
12def parse_search_query(query):
13 q = []
14 for word in query.split():
15 # Is this lexeme negated?
16 negated = word.startswith("!")
17
18 # Remove any special characters
19 word = re.sub(r"\W+", "", word, flags=re.UNICODE)
20 if not word:
21 continue
22
23 # Restore negation
24 if negated:
25 word = "!%s" % word
26
27 q.append(word)
28
29 return " & ".join(q)
30
84604476
MT
31def format_size(s, max_unit=None):
32 units = ("B", "kB", "MB", "GB", "TB")
66862195
MT
33
34 i = 0
35 while s >= 1024 and i < len(units) - 1:
36 s /= 1024
37 i += 1
38
84604476
MT
39 if max_unit and units[i] == max_unit:
40 break
41
66862195
MT
42 return "%.0f%s" % (s, units[i])
43
5ac74b02 44def format_time(s, shorter=True):
66862195
MT
45 #_ = handler.locale.translate
46 _ = lambda x: x
47
48 hrs, s = divmod(s, 3600)
49 min, s = divmod(s, 60)
50
51 if s >= 30:
52 min += 1
53
54 if shorter and not hrs:
55 return _("%(min)d min") % { "min" : min }
56
57 return _("%(hrs)d:%(min)02d hrs") % {"hrs" : hrs, "min" : min}
e96e445b
MT
58
59def random_string(length=8):
60 input_chars = string.ascii_letters + string.digits
61
62 r = (random.choice(input_chars) for i in range(length))
63
64 return "".join(r)
75d9b3da
MT
65
66def normalize(s):
67 # Remove any non-ASCII characters
68 try:
69 s = unicodedata.normalize("NFKD", s)
70 except TypeError:
71 pass
72
73 # Remove excessive whitespace
74 s = re.sub(r"[^\w]+", " ", s)
75
76 return "-".join(s.split())
5ef115cd
MT
77
78def generate_thumbnail(data, size, **args):
79 assert data, "No image data received"
80
81 image = PIL.Image.open(io.BytesIO(data))
82
83 # Save image format
84 format = image.format
85
86 # Remove any alpha-channels
87 if image.format == "JPEG" and not image.mode == "RGB":
88 # Make a white background
89 background = PIL.Image.new("RGBA", image.size, (255,255,255))
90
91 # Convert image to RGBA if not in RGBA, yet
92 if not image.mode == "RGBA":
93 image = image.convert("RGBA")
94
95 # Flatten both images together
96 flattened_image = PIL.Image.alpha_composite(background, image)
97
98 # Remove the alpha channel
99 image = flattened_image.convert("RGB")
100
101 # Resize the image to the desired resolution
102 image.thumbnail((size, size), PIL.Image.LANCZOS)
103
104 if image.format == "JPEG":
105 # Apply a gaussian blur to make compression easier
106 image = image.filter(PIL.ImageFilter.GaussianBlur(radius=0.05))
107
108 # Arguments to optimise the compression
109 args.update({
110 "subsampling" : "4:2:0",
111 "quality" : 70,
112 })
113
114 with io.BytesIO() as f:
115 # If writing out the image does not work with optimization,
116 # we try to write it out without any optimization.
117 try:
118 image.save(f, format, optimize=True, **args)
119 except:
120 image.save(f, format, **args)
121
122 return f.getvalue()