viewers.py
210 lines
| 5.7 KiB
| text/x-python
|
PythonLexer
neko259
|
r1715 | import re | ||
neko259
|
r1684 | |||
neko259
|
r1716 | from django.contrib.staticfiles import finders | ||
from django.contrib.staticfiles.templatetags.staticfiles import static | ||||
neko259
|
r1590 | from django.core.files.images import get_image_dimensions | ||
neko259
|
r1273 | from django.template.defaultfilters import filesizeformat | ||
neko259
|
r1716 | |||
neko259
|
r1772 | from boards.utils import get_domain, cached_result | ||
neko259
|
r1273 | |||
neko259
|
r1715 | |||
neko259
|
r1279 | FILE_STUB_IMAGE = 'images/file.png' | ||
neko259
|
r1677 | FILE_STUB_URL = 'url' | ||
neko259
|
r1279 | |||
neko259
|
r1727 | |||
neko259
|
r1307 | FILE_TYPES_VIDEO = ( | ||
'webm', | ||||
'mp4', | ||||
neko259
|
r1342 | 'mpeg', | ||
neko259
|
r1493 | 'ogv', | ||
neko259
|
r1307 | ) | ||
neko259
|
r1284 | FILE_TYPE_SVG = 'svg' | ||
neko259
|
r1279 | FILE_TYPES_AUDIO = ( | ||
'ogg', | ||||
'mp3', | ||||
neko259
|
r1339 | 'opus', | ||
neko259
|
r1279 | ) | ||
neko259
|
r1590 | FILE_TYPES_IMAGE = ( | ||
'jpeg', | ||||
'jpg', | ||||
'png', | ||||
'bmp', | ||||
'gif', | ||||
) | ||||
neko259
|
r1279 | |||
neko259
|
r1326 | PLAIN_FILE_FORMATS = { | ||
'pdf': 'pdf', | ||||
'djvu': 'djvu', | ||||
'txt': 'txt', | ||||
neko259
|
r1665 | 'tex': 'tex', | ||
'xcf': 'xcf', | ||||
neko259
|
r1713 | 'zip': 'archive', | ||
'tar': 'archive', | ||||
'gz': 'archive', | ||||
neko259
|
r1326 | } | ||
neko259
|
r1677 | URL_PROTOCOLS = { | ||
'magnet': 'magnet', | ||||
} | ||||
neko259
|
r1590 | CSS_CLASS_IMAGE = 'image' | ||
CSS_CLASS_THUMB = 'thumb' | ||||
neko259
|
r1273 | |||
neko259
|
r1286 | def get_viewers(): | ||
return AbstractViewer.__subclasses__() | ||||
neko259
|
r1684 | def get_static_dimensions(filename): | ||
file_path = finders.find(filename) | ||||
return get_image_dimensions(file_path) | ||||
neko259
|
r1718 | # TODO Move this to utils | ||
def file_exists(filename): | ||||
return finders.find(filename) is not None | ||||
neko259
|
r1273 | class AbstractViewer: | ||
neko259
|
r1660 | def __init__(self, file, file_type, hash, url): | ||
neko259
|
r1273 | self.file = file | ||
self.file_type = file_type | ||||
neko259
|
r1596 | self.hash = hash | ||
neko259
|
r1660 | self.url = url | ||
neko259
|
r1273 | |||
@staticmethod | ||||
def supports(file_type): | ||||
neko259
|
r1279 | return True | ||
neko259
|
r1273 | |||
def get_view(self): | ||||
neko259
|
r1300 | return '<div class="image">'\ | ||
'{}'\ | ||||
neko259
|
r1308 | '<div class="image-metadata"><a href="{}" download >{}, {}</a></div>'\ | ||
'</div>'.format(self.get_format_view(), self.file.url, | ||||
neko259
|
r1300 | self.file_type, filesizeformat(self.file.size)) | ||
def get_format_view(self): | ||||
neko259
|
r1326 | if self.file_type in PLAIN_FILE_FORMATS: | ||
image = 'images/fileformats/{}.png'.format( | ||||
PLAIN_FILE_FORMATS[self.file_type]) | ||||
else: | ||||
image = FILE_STUB_IMAGE | ||||
neko259
|
r1684 | w, h = get_static_dimensions(image) | ||
neko259
|
r1300 | return '<a href="{}">'\ | ||
neko259
|
r1684 | '<img class="url-image" src="{}" width="{}" height="{}"/>'\ | ||
'</a>'.format(self.file.url, static(image), w, h) | ||||
neko259
|
r1273 | |||
neko259
|
r1307 | class VideoViewer(AbstractViewer): | ||
neko259
|
r1273 | @staticmethod | ||
def supports(file_type): | ||||
neko259
|
r1307 | return file_type in FILE_TYPES_VIDEO | ||
neko259
|
r1273 | |||
neko259
|
r1300 | def get_format_view(self): | ||
return '<video width="200" height="150" controls src="{}"></video>'\ | ||||
.format(self.file.url) | ||||
neko259
|
r1273 | |||
neko259
|
r1279 | |||
class AudioViewer(AbstractViewer): | ||||
@staticmethod | ||||
def supports(file_type): | ||||
return file_type in FILE_TYPES_AUDIO | ||||
neko259
|
r1300 | def get_format_view(self): | ||
return '<audio controls src="{}"></audio>'.format(self.file.url) | ||||
neko259
|
r1284 | |||
class SvgViewer(AbstractViewer): | ||||
@staticmethod | ||||
def supports(file_type): | ||||
return file_type == FILE_TYPE_SVG | ||||
neko259
|
r1300 | def get_format_view(self): | ||
return '<a class="thumb" href="{}">'\ | ||||
'<img class="post-image-preview" width="200" height="150" src="{}" />'\ | ||||
'</a>'.format(self.file.url, self.file.url) | ||||
neko259
|
r1590 | |||
class ImageViewer(AbstractViewer): | ||||
@staticmethod | ||||
def supports(file_type): | ||||
return file_type in FILE_TYPES_IMAGE | ||||
def get_format_view(self): | ||||
metadata = '{}, {}'.format(self.file.name.split('.')[-1], | ||||
filesizeformat(self.file.size)) | ||||
width, height = get_image_dimensions(self.file.file) | ||||
preview_path = self.file.path.replace('.', '.200x150.') | ||||
pre_width, pre_height = get_image_dimensions(preview_path) | ||||
split = self.file.url.rsplit('.', 1) | ||||
w, h = 200, 150 | ||||
thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1]) | ||||
return '<a class="{}" href="{full}">' \ | ||||
'<img class="post-image-preview"' \ | ||||
' src="{}"' \ | ||||
neko259
|
r1596 | ' alt="{}"' \ | ||
neko259
|
r1590 | ' width="{}"' \ | ||
' height="{}"' \ | ||||
' data-width="{}"' \ | ||||
' data-height="{}" />' \ | ||||
'</a>' \ | ||||
.format(CSS_CLASS_THUMB, | ||||
thumb_url, | ||||
neko259
|
r1596 | self.hash, | ||
neko259
|
r1590 | str(pre_width), | ||
str(pre_height), str(width), str(height), | ||||
full=self.file.url, image_meta=metadata) | ||||
neko259
|
r1660 | |||
class UrlViewer(AbstractViewer): | ||||
@staticmethod | ||||
def supports(file_type): | ||||
return file_type is None | ||||
def get_view(self): | ||||
return '<div class="image">' \ | ||||
'{}' \ | ||||
neko259
|
r1765 | '<div class="image-metadata">{}</div>' \ | ||
'</div>'.format(self.get_format_view(), get_domain(self.url)) | ||||
neko259
|
r1677 | |||
neko259
|
r1660 | def get_format_view(self): | ||
neko259
|
r1677 | protocol = self.url.split('://')[0] | ||
neko259
|
r1765 | |||
domain = get_domain(self.url) | ||||
neko259
|
r1695 | |||
if protocol in URL_PROTOCOLS: | ||||
url_image_name = URL_PROTOCOLS.get(protocol) | ||||
neko259
|
r1765 | elif domain: | ||
neko259
|
r1772 | url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL | ||
neko259
|
r1765 | else: | ||
url_image_name = FILE_STUB_URL | ||||
neko259
|
r1695 | |||
neko259
|
r1718 | image_path = 'images/{}.png'.format(url_image_name) | ||
image = static(image_path) | ||||
w, h = get_static_dimensions(image_path) | ||||
neko259
|
r1684 | |||
neko259
|
r1660 | return '<a href="{}">' \ | ||
neko259
|
r1684 | '<img class="url-image" src="{}" width="{}" height="{}"/>' \ | ||
'</a>'.format(self.url, image, w, h) | ||||
neko259
|
r1765 | |||
neko259
|
r1772 | @cached_result() | ||
def _find_image_for_domains(self, domain): | ||||
""" | ||||
Searches for the domain image for every domain level except top. | ||||
E.g. for l3.example.co.uk it will search for l3.example.co.uk, then | ||||
example.co.uk, then co.uk | ||||
""" | ||||
levels = domain.split('.') | ||||
while len(levels) > 1: | ||||
domain = '.'.join(levels) | ||||
neko259
|
r1765 | |||
neko259
|
r1772 | filename = 'images/domains/{}.png'.format(domain) | ||
if file_exists(filename): | ||||
return 'domains/' + domain | ||||
else: | ||||
del levels[0] | ||||
neko259
|
r1765 | |||