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