##// END OF EJS Templates
Fixed SVG viewer
neko259 -
r1908:810844f7 default
parent child Browse files
Show More
@@ -1,244 +1,244 b''
1 import re
1 import re
2
2
3 from PIL import Image
3 from PIL import Image
4
4
5 from django.contrib.staticfiles import finders
5 from django.contrib.staticfiles import finders
6 from django.contrib.staticfiles.templatetags.staticfiles import static
6 from django.contrib.staticfiles.templatetags.staticfiles import static
7 from django.core.files.images import get_image_dimensions
7 from django.core.files.images import get_image_dimensions
8 from django.template.defaultfilters import filesizeformat
8 from django.template.defaultfilters import filesizeformat
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
11
11
12 from boards.utils import get_domain, cached_result, get_extension
12 from boards.utils import get_domain, cached_result, get_extension
13 from boards import settings
13 from boards import settings
14
14
15
15
16 FILE_STUB_IMAGE = 'images/file.png'
16 FILE_STUB_IMAGE = 'images/file.png'
17 FILE_STUB_URL = 'url'
17 FILE_STUB_URL = 'url'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
19
19
20
20
21 FILE_TYPES_VIDEO = (
21 FILE_TYPES_VIDEO = (
22 'video/webm',
22 'video/webm',
23 'video/mp4',
23 'video/mp4',
24 'video/mpeg',
24 'video/mpeg',
25 'video/ogv',
25 'video/ogv',
26 )
26 )
27 FILE_TYPE_SVG = 'image/svg'
27 FILE_TYPE_SVG = 'image/svg+xml'
28 FILE_TYPES_AUDIO = (
28 FILE_TYPES_AUDIO = (
29 'audio/ogg',
29 'audio/ogg',
30 'audio/mpeg',
30 'audio/mpeg',
31 'audio/opus',
31 'audio/opus',
32 'audio/x-flac',
32 'audio/x-flac',
33 'audio/mpeg',
33 'audio/mpeg',
34 )
34 )
35 FILE_TYPES_IMAGE = (
35 FILE_TYPES_IMAGE = (
36 'image/jpeg',
36 'image/jpeg',
37 'image/jpg',
37 'image/jpg',
38 'image/png',
38 'image/png',
39 'image/bmp',
39 'image/bmp',
40 'image/gif',
40 'image/gif',
41 )
41 )
42
42
43 PLAIN_FILE_FORMATS = {
43 PLAIN_FILE_FORMATS = {
44 'zip': 'archive',
44 'zip': 'archive',
45 'tar': 'archive',
45 'tar': 'archive',
46 'gz': 'archive',
46 'gz': 'archive',
47 'mid' : 'midi',
47 'mid' : 'midi',
48 }
48 }
49
49
50 URL_PROTOCOLS = {
50 URL_PROTOCOLS = {
51 'magnet': 'magnet',
51 'magnet': 'magnet',
52 }
52 }
53
53
54 CSS_CLASS_IMAGE = 'image'
54 CSS_CLASS_IMAGE = 'image'
55 CSS_CLASS_THUMB = 'thumb'
55 CSS_CLASS_THUMB = 'thumb'
56
56
57 ABSTRACT_VIEW = '<div class="image">'\
57 ABSTRACT_VIEW = '<div class="image">'\
58 '{}'\
58 '{}'\
59 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
59 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
60 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}">πŸ” </a></div>'\
60 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}">πŸ” </a></div>'\
61 '</div>'
61 '</div>'
62 URL_VIEW = '<div class="image">' \
62 URL_VIEW = '<div class="image">' \
63 '{}' \
63 '{}' \
64 '<div class="image-metadata">{}</div>' \
64 '<div class="image-metadata">{}</div>' \
65 '</div>'
65 '</div>'
66 ABSTRACT_FORMAT_VIEW = '<a href="{}">'\
66 ABSTRACT_FORMAT_VIEW = '<a href="{}">'\
67 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
67 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
68 '</a>'
68 '</a>'
69 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
69 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
70 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
70 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
71 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
71 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
72 '<img class="post-image-preview"' \
72 '<img class="post-image-preview"' \
73 ' src="{}"' \
73 ' src="{}"' \
74 ' alt="{}"' \
74 ' alt="{}"' \
75 ' width="{}"' \
75 ' width="{}"' \
76 ' height="{}"' \
76 ' height="{}"' \
77 ' data-width="{}"' \
77 ' data-width="{}"' \
78 ' data-height="{}" />' \
78 ' data-height="{}" />' \
79 '</a>'
79 '</a>'
80 SVG_FORMAT_VIEW = '<a class="thumb" href="{}">'\
80 SVG_FORMAT_VIEW = '<a class="thumb" href="{}">'\
81 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
81 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
82 '</a>'
82 '</a>'
83 URL_FORMAT_VIEW = '<a href="{}">' \
83 URL_FORMAT_VIEW = '<a href="{}">' \
84 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
84 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
85 '</a>'
85 '</a>'
86
86
87
87
88 def get_viewers():
88 def get_viewers():
89 return AbstractViewer.__subclasses__()
89 return AbstractViewer.__subclasses__()
90
90
91
91
92 def get_static_dimensions(filename):
92 def get_static_dimensions(filename):
93 file_path = finders.find(filename)
93 file_path = finders.find(filename)
94 return get_image_dimensions(file_path)
94 return get_image_dimensions(file_path)
95
95
96
96
97 # TODO Move this to utils
97 # TODO Move this to utils
98 def file_exists(filename):
98 def file_exists(filename):
99 return finders.find(filename) is not None
99 return finders.find(filename) is not None
100
100
101
101
102 class AbstractViewer:
102 class AbstractViewer:
103 def __init__(self, file, file_type, hash, url):
103 def __init__(self, file, file_type, hash, url):
104 self.file = file
104 self.file = file
105 self.file_type = file_type
105 self.file_type = file_type
106 self.hash = hash
106 self.hash = hash
107 self.url = url
107 self.url = url
108 self.extension = get_extension(self.file.name)
108 self.extension = get_extension(self.file.name)
109
109
110 @staticmethod
110 @staticmethod
111 def supports(file_type):
111 def supports(file_type):
112 return True
112 return True
113
113
114 def get_view(self):
114 def get_view(self):
115 search_host = settings.get('External', 'ImageSearchHost')
115 search_host = settings.get('External', 'ImageSearchHost')
116 if search_host:
116 if search_host:
117 if search_host.endswith('/'):
117 if search_host.endswith('/'):
118 search_host = search_host[:-1]
118 search_host = search_host[:-1]
119 search_url = search_host + self.file.url
119 search_url = search_host + self.file.url
120 else:
120 else:
121 search_url = ''
121 search_url = ''
122
122
123 return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url,
123 return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url,
124 self.file_type, filesizeformat(self.file.size),
124 self.file_type, filesizeformat(self.file.size),
125 self.file_type, search_url, self.file.name)
125 self.file_type, search_url, self.file.name)
126
126
127 def get_format_view(self):
127 def get_format_view(self):
128 image_name = PLAIN_FILE_FORMATS.get(self.extension, self.extension)
128 image_name = PLAIN_FILE_FORMATS.get(self.extension, self.extension)
129 file_name = FILE_FILEFORMAT.format(image_name)
129 file_name = FILE_FILEFORMAT.format(image_name)
130
130
131 if file_exists(file_name):
131 if file_exists(file_name):
132 image = file_name
132 image = file_name
133 else:
133 else:
134 image = FILE_STUB_IMAGE
134 image = FILE_STUB_IMAGE
135
135
136 w, h = get_static_dimensions(image)
136 w, h = get_static_dimensions(image)
137
137
138 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
138 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
139
139
140
140
141 class VideoViewer(AbstractViewer):
141 class VideoViewer(AbstractViewer):
142 @staticmethod
142 @staticmethod
143 def supports(file_type):
143 def supports(file_type):
144 return file_type in FILE_TYPES_VIDEO
144 return file_type in FILE_TYPES_VIDEO
145
145
146 def get_format_view(self):
146 def get_format_view(self):
147 return VIDEO_FORMAT_VIEW.format(self.file.url)
147 return VIDEO_FORMAT_VIEW.format(self.file.url)
148
148
149
149
150 class AudioViewer(AbstractViewer):
150 class AudioViewer(AbstractViewer):
151 @staticmethod
151 @staticmethod
152 def supports(file_type):
152 def supports(file_type):
153 return file_type in FILE_TYPES_AUDIO
153 return file_type in FILE_TYPES_AUDIO
154
154
155 def get_format_view(self):
155 def get_format_view(self):
156 return AUDIO_FORMAT_VIEW.format(self.file.url)
156 return AUDIO_FORMAT_VIEW.format(self.file.url)
157
157
158
158
159 class SvgViewer(AbstractViewer):
159 class SvgViewer(AbstractViewer):
160 @staticmethod
160 @staticmethod
161 def supports(file_type):
161 def supports(file_type):
162 return file_type == FILE_TYPE_SVG
162 return file_type == FILE_TYPE_SVG
163
163
164 def get_format_view(self):
164 def get_format_view(self):
165 return SVG_FORMAT_VIEW.format(self.file.url, self.file.url)
165 return SVG_FORMAT_VIEW.format(self.file.url, self.file.url)
166
166
167
167
168 class ImageViewer(AbstractViewer):
168 class ImageViewer(AbstractViewer):
169 @staticmethod
169 @staticmethod
170 def supports(file_type):
170 def supports(file_type):
171 return file_type in FILE_TYPES_IMAGE
171 return file_type in FILE_TYPES_IMAGE
172
172
173 def get_format_view(self):
173 def get_format_view(self):
174 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
174 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
175 filesizeformat(self.file.size))
175 filesizeformat(self.file.size))
176
176
177 try:
177 try:
178 width, height = get_image_dimensions(self.file.path)
178 width, height = get_image_dimensions(self.file.path)
179 except Exception:
179 except Exception:
180 # If the image is a decompression bomb, treat it as just a regular
180 # If the image is a decompression bomb, treat it as just a regular
181 # file
181 # file
182 return super().get_format_view()
182 return super().get_format_view()
183
183
184 preview_path = self.file.path.replace('.', '.200x150.')
184 preview_path = self.file.path.replace('.', '.200x150.')
185 try:
185 try:
186 pre_width, pre_height = get_image_dimensions(preview_path)
186 pre_width, pre_height = get_image_dimensions(preview_path)
187 except Exception:
187 except Exception:
188 return super().get_format_view()
188 return super().get_format_view()
189
189
190 split = self.file.url.rsplit('.', 1)
190 split = self.file.url.rsplit('.', 1)
191 w, h = 200, 150
191 w, h = 200, 150
192 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
192 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
193
193
194 return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB,
194 return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB,
195 thumb_url,
195 thumb_url,
196 self.hash,
196 self.hash,
197 str(pre_width),
197 str(pre_width),
198 str(pre_height), str(width), str(height),
198 str(pre_height), str(width), str(height),
199 full=self.file.url, image_meta=metadata)
199 full=self.file.url, image_meta=metadata)
200
200
201
201
202 class UrlViewer(AbstractViewer):
202 class UrlViewer(AbstractViewer):
203 @staticmethod
203 @staticmethod
204 def supports(file_type):
204 def supports(file_type):
205 return file_type is None
205 return file_type is None
206
206
207 def get_view(self):
207 def get_view(self):
208 return URL_VIEW.format(self.get_format_view(), get_domain(self.url))
208 return URL_VIEW.format(self.get_format_view(), get_domain(self.url))
209
209
210 def get_format_view(self):
210 def get_format_view(self):
211 protocol = self.url.split(':')[0]
211 protocol = self.url.split(':')[0]
212
212
213 domain = get_domain(self.url)
213 domain = get_domain(self.url)
214
214
215 if protocol in URL_PROTOCOLS:
215 if protocol in URL_PROTOCOLS:
216 url_image_name = URL_PROTOCOLS.get(protocol)
216 url_image_name = URL_PROTOCOLS.get(protocol)
217 elif domain:
217 elif domain:
218 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
218 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
219 else:
219 else:
220 url_image_name = FILE_STUB_URL
220 url_image_name = FILE_STUB_URL
221
221
222 image_path = 'images/{}.png'.format(url_image_name)
222 image_path = 'images/{}.png'.format(url_image_name)
223 image = static(image_path)
223 image = static(image_path)
224 w, h = get_static_dimensions(image_path)
224 w, h = get_static_dimensions(image_path)
225
225
226 return URL_FORMAT_VIEW.format(self.url, image, w, h)
226 return URL_FORMAT_VIEW.format(self.url, image, w, h)
227
227
228 @cached_result()
228 @cached_result()
229 def _find_image_for_domains(self, domain):
229 def _find_image_for_domains(self, domain):
230 """
230 """
231 Searches for the domain image for every domain level except top.
231 Searches for the domain image for every domain level except top.
232 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
232 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
233 example.co.uk, then co.uk
233 example.co.uk, then co.uk
234 """
234 """
235 levels = domain.split('.')
235 levels = domain.split('.')
236 while len(levels) > 1:
236 while len(levels) > 1:
237 domain = '.'.join(levels)
237 domain = '.'.join(levels)
238
238
239 filename = 'images/domains/{}.png'.format(domain)
239 filename = 'images/domains/{}.png'.format(domain)
240 if file_exists(filename):
240 if file_exists(filename):
241 return 'domains/' + domain
241 return 'domains/' + domain
242 else:
242 else:
243 del levels[0]
243 del levels[0]
244
244
General Comments 0
You need to be logged in to leave comments. Login now