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