from django.contrib.staticfiles import finders from django.contrib.staticfiles.templatetags.staticfiles import static from django.core.files.images import get_image_dimensions from django.template.defaultfilters import filesizeformat from boards import settings from boards.abstracts.constants import THUMB_SIZES from boards.settings import SECTION_EXTERNAL from boards.utils import get_domain, cached_result, get_extension PROTOCOL_DELIMITER = ':' DOMAIN_DELIMITER = '.' FILE_STUB_IMAGE = 'images/file.png' FILE_STUB_URL = 'url' FILE_FILEFORMAT = 'images/fileformats/{}.png' FILE_TYPES_VIDEO = ( 'video/webm', 'video/mp4', 'video/mpeg', 'video/ogv', ) FILE_TYPE_SVG = 'image/svg+xml' FILE_TYPES_AUDIO = ( 'audio/ogg', 'audio/mpeg', 'audio/opus', 'audio/x-flac', 'audio/mpeg', ) FILE_TYPES_IMAGE = ( 'image/jpeg', 'image/jpg', 'image/png', 'image/bmp', 'image/gif', ) PLAIN_FILE_FORMATS = { 'zip': 'archive', 'tar': 'archive', 'gz': 'archive', 'mid' : 'midi', } URL_PROTOCOLS = { 'magnet': 'magnet', } CSS_CLASS_IMAGE = 'image' CSS_CLASS_THUMB = 'thumb' ABSTRACT_VIEW = '
'\ '{}'\ '
{}, {}'\ '
'\ '
' URL_VIEW = '
' \ '{}' \ '
{}
' \ '
' ABSTRACT_FORMAT_VIEW = ''\ ''\ '' VIDEO_FORMAT_VIEW = '' AUDIO_FORMAT_VIEW = '' IMAGE_FORMAT_VIEW = '' \ '{}' \ '' SVG_FORMAT_VIEW = ''\ ''\ '' URL_FORMAT_VIEW = '' \ '' \ '' def get_viewers(): return AbstractViewer.__subclasses__() class AbstractViewer: def __init__(self, file, file_type, id, url): self.file = file self.file_type = file_type self.id = id self.url = url self.extension = get_extension(self.file.name).lower() @staticmethod def supports(file_type): return True def get_view(self): return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url, self.file_type, filesizeformat(self.file.size), self.file_type, self._get_search_url(), self.file.name, self.id) def _get_search_url(self): search_host = settings.get(SECTION_EXTERNAL, 'ImageSearchHost') if search_host: if search_host.endswith('/'): search_host = search_host[:-1] search_url = search_host + self.file.url else: search_url = '' return search_url def get_format_view(self): image_name = PLAIN_FILE_FORMATS.get(self.extension, self.extension) file_name = FILE_FILEFORMAT.format(image_name) if self.file_exists(file_name): image = file_name else: image = FILE_STUB_IMAGE w, h = self.get_static_dimensions(image) return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h) @cached_result() def get_static_dimensions(self, filename): file_path = finders.find(filename) return get_image_dimensions(file_path) @cached_result() def file_exists(self, filename): return finders.find(filename) is not None class VideoViewer(AbstractViewer): @staticmethod def supports(file_type): return file_type in FILE_TYPES_VIDEO def get_format_view(self): return VIDEO_FORMAT_VIEW.format(self.file.url) class AudioViewer(AbstractViewer): @staticmethod def supports(file_type): return file_type in FILE_TYPES_AUDIO def get_format_view(self): return AUDIO_FORMAT_VIEW.format(self.file.url) class SvgViewer(AbstractViewer): @staticmethod def supports(file_type): return file_type == FILE_TYPE_SVG def get_format_view(self): return SVG_FORMAT_VIEW.format(self.file.url, self.file.url) 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)) try: width, height = get_image_dimensions(self.file.path) except Exception: # If the image is a decompression bomb, treat it as just a regular # file return super().get_format_view() preview_path = self.file.path.replace('.', '.200x150.') try: pre_width, pre_height = get_image_dimensions(preview_path) except Exception: return super().get_format_view() split = self.file.url.rsplit('.', 1) w, h = THUMB_SIZES[0] thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1]) return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB, thumb_url, self.id, str(pre_width), str(pre_height), str(width), str(height), full=self.file.url, image_meta=metadata) class UrlViewer(AbstractViewer): @staticmethod def supports(file_type): return file_type is None def get_view(self): return URL_VIEW.format(self.get_format_view(), get_domain(self.url)) def get_format_view(self): protocol = self.url.split(PROTOCOL_DELIMITER)[0] domain = get_domain(self.url) image_path = 'images/{}.png'.format(self._get_image_name(protocol, domain)) image = static(image_path) w, h = self.get_static_dimensions(image_path) return URL_FORMAT_VIEW.format(self.url, image, w, h) 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(DOMAIN_DELIMITER) while len(levels) > 1: domain = DOMAIN_DELIMITER.join(levels) filename = 'images/domains/{}.png'.format(domain) if self.file_exists(filename): return 'domains/' + domain else: del levels[0] @cached_result() def _get_image_name(self, protocol, domain): if protocol in URL_PROTOCOLS: url_image_name = URL_PROTOCOLS.get(protocol) elif domain: url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL else: url_image_name = FILE_STUB_URL return url_image_name