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