mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Add custom thumbnailer
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PretixHelpersConfig(AppConfig):
|
||||
name = 'pretix.helpers'
|
||||
label = 'pretixhelpers'
|
||||
|
||||
|
||||
default_app_config = 'pretix.helpers.PretixHelpersConfig'
|
||||
|
||||
29
src/pretix/helpers/migrations/0001_initial.py
Normal file
29
src/pretix/helpers/migrations/0001_initial.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-03-20 07:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Thumbnail',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('source', models.CharField(max_length=255)),
|
||||
('size', models.CharField(max_length=255)),
|
||||
('thumb', models.FileField(upload_to='')),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='thumbnail',
|
||||
unique_together=set([('source', 'size')]),
|
||||
),
|
||||
]
|
||||
0
src/pretix/helpers/migrations/__init__.py
Normal file
0
src/pretix/helpers/migrations/__init__.py
Normal file
10
src/pretix/helpers/models.py
Normal file
10
src/pretix/helpers/models.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Thumbnail(models.Model):
|
||||
source = models.CharField(max_length=255)
|
||||
size = models.CharField(max_length=255)
|
||||
thumb = models.FileField(upload_to='pub/thumbs/')
|
||||
|
||||
class Meta:
|
||||
unique_together = (('source', 'size'),)
|
||||
0
src/pretix/helpers/templatetags/__init__.py
Normal file
0
src/pretix/helpers/templatetags/__init__.py
Normal file
18
src/pretix/helpers/templatetags/thumb.py
Normal file
18
src/pretix/helpers/templatetags/thumb.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import logging
|
||||
|
||||
from django import template
|
||||
from django.core.files.storage import default_storage
|
||||
|
||||
from pretix.helpers.thumb import get_thumbnail
|
||||
|
||||
register = template.Library()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register.filter
|
||||
def thumb(source, arg):
|
||||
try:
|
||||
return get_thumbnail(source, arg).thumb.url
|
||||
except:
|
||||
logger.exception('Failed to create thumbnail')
|
||||
return default_storage.url(source)
|
||||
75
src/pretix/helpers/thumb.py
Normal file
75
src/pretix/helpers/thumb.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import hashlib
|
||||
from io import BytesIO
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from PIL import Image
|
||||
|
||||
from pretix.helpers.models import Thumbnail
|
||||
|
||||
|
||||
class ThumbnailError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_sizes(size, imgsize):
|
||||
crop = False
|
||||
if size.endswith('^'):
|
||||
crop = True
|
||||
size = size[:-1]
|
||||
|
||||
if 'x' in size:
|
||||
size = [int(p) for p in size.split('x')]
|
||||
else:
|
||||
size = [int(size), int(size)]
|
||||
|
||||
if crop:
|
||||
wfactor = min(1, size[0] / imgsize[0])
|
||||
hfactor = min(1, size[1] / imgsize[1])
|
||||
if wfactor > hfactor:
|
||||
return (int(size[0]), int(imgsize[1] * hfactor)), \
|
||||
(0, int((imgsize[1] * wfactor - size[1]) / 2), size[0], int((imgsize[1] * wfactor + size[1]) / 2))
|
||||
else:
|
||||
return (int(imgsize[0] * hfactor), int(size[1])), \
|
||||
(int((imgsize[0] * hfactor - size[0]) / 2), 0, int((imgsize[0] * hfactor + size[0]) / 2), size[1])
|
||||
else:
|
||||
wfactor = min(1, size[0] / imgsize[0])
|
||||
hfactor = min(1, size[1] / imgsize[1])
|
||||
if wfactor < hfactor:
|
||||
return (size[0], int(imgsize[1] * wfactor)), None
|
||||
else:
|
||||
return (int(imgsize[0] * hfactor), size[1]), None
|
||||
|
||||
|
||||
def create_thumbnail(source, size):
|
||||
if isinstance(source, str):
|
||||
source = default_storage.open(source)
|
||||
image = Image.open(BytesIO(source.read()))
|
||||
try:
|
||||
image.load()
|
||||
except:
|
||||
raise ThumbnailError('Could not load image')
|
||||
|
||||
scale, crop = get_sizes(size, image.size)
|
||||
image = image.resize(scale)
|
||||
print(scale, crop)
|
||||
if crop:
|
||||
image = image.crop(crop)
|
||||
|
||||
checksum = hashlib.md5(image.tobytes()).hexdigest()
|
||||
name = checksum + '.' + size.replace('^', 'c') + '.png'
|
||||
buffer = BytesIO()
|
||||
image.save(fp=buffer, format='PNG')
|
||||
imgfile = ContentFile(buffer.getvalue())
|
||||
|
||||
t = Thumbnail.objects.create(source=source, size=size)
|
||||
t.thumb.save(name, imgfile)
|
||||
return t
|
||||
|
||||
|
||||
def get_thumbnail(source, size):
|
||||
# Assumes files are immutable
|
||||
try:
|
||||
return Thumbnail.objects.get(source=source, size=size)
|
||||
except Thumbnail.DoesNotExist:
|
||||
return create_thumbnail(source, size)
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "pretixpresale/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load thumbnail %}
|
||||
{% load thumb %}
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% block thetitle %}
|
||||
@@ -27,7 +27,7 @@
|
||||
{% if event_logo %}
|
||||
<a href="{% eventurl event "presale:event.index" cart_namespace=cart_namespace|default_if_none:"" %}"
|
||||
title="{{ event.name }}">
|
||||
<img src="{{ event_logo|thumbnail_url:'logo' }}" alt="{{ event.name }}" class="event-logo" />
|
||||
<img src="{{ event_logo|thumb:'5000x120' }}" alt="{{ event.name }}" class="event-logo" />
|
||||
</a>
|
||||
{% else %}
|
||||
<h1>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% load l10n %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% load thumbnail %}
|
||||
{% load thumb %}
|
||||
{% load eventsignal %}
|
||||
{% load rich_text %}
|
||||
{% block title %}{% trans "Presale" %}{% endblock %}
|
||||
@@ -225,7 +225,7 @@
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}">
|
||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||
<img src="{{ item.picture.name|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -348,7 +348,7 @@
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}">
|
||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||
<img src="{{ item.picture.name|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{% load money %}
|
||||
{% load eventurl %}
|
||||
{% load eventsignal %}
|
||||
{% load thumbnail %}
|
||||
{% load thumb %}
|
||||
{% load rich_text %}
|
||||
{% block title %}{% trans "Voucher redemption" %}{% endblock %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}">
|
||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||
<img src="{{ item.picture|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -139,7 +139,7 @@
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}">
|
||||
<img src="{{ item.picture|thumbnail_url:'productlist' }}"
|
||||
<img src="{{ item.picture|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n eventurl thumbnail bootstrap3 %}
|
||||
{% load i18n eventurl bootstrap3 %}
|
||||
{% block title %}{% trans "Waiting list" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>{% trans "Add me to the waiting list" %}</h2>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "pretixpresale/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load thumbnail %}
|
||||
{% load thumb %}
|
||||
{% load eventurl %}
|
||||
{% block thetitle %}
|
||||
{% block title %}{% endblock %}{% if url_name != "organizer.index" %} :: {% endif %}{{ organizer.name }}
|
||||
@@ -11,7 +11,8 @@
|
||||
<div class="pull-left">
|
||||
{% if organizer_logo %}
|
||||
<a href="{% eventurl organizer "presale:organizer.index" %}" title="{{ organizer.name }}">
|
||||
<img src="{{ organizer_logo|thumbnail_url:'logo' }}" alt="{{ organizer.name }}" class="organizer-logo" />
|
||||
<img src="{{ organizer_logo|thumb:'5000x120' }}" alt="{{ organizer.name }}"
|
||||
class="organizer-logo" />
|
||||
</a>
|
||||
{% else %}
|
||||
<h1><a href="{% eventurl organizer "presale:organizer.index" %}">{{ organizer.name }}</a></h1>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import hashlib
|
||||
import json
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
@@ -19,7 +18,6 @@ from django.views.decorators.http import condition
|
||||
from django.views.i18n import (
|
||||
get_formats, get_javascript_catalog, js_catalog_template,
|
||||
)
|
||||
from easy_thumbnails.files import get_thumbnailer
|
||||
from lxml import etree
|
||||
|
||||
from pretix.base.i18n import language
|
||||
@@ -27,6 +25,7 @@ from pretix.base.models import CartPosition, Voucher
|
||||
from pretix.base.services.cart import error_messages
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.helpers.thumb import get_thumbnail
|
||||
from pretix.presale.views.cart import get_or_create_cart_id
|
||||
from pretix.presale.views.event import (
|
||||
get_grouped_items, item_group_by_category,
|
||||
@@ -136,8 +135,7 @@ def price_dict(price):
|
||||
|
||||
|
||||
def get_picture(picture):
|
||||
thumb = get_thumbnailer(picture)['productlist']
|
||||
return urljoin(settings.SITE_URL, thumb.url)
|
||||
return get_thumbnail(picture.name, '60x60^').thumb.url
|
||||
|
||||
|
||||
class WidgetAPIProductList(View):
|
||||
|
||||
@@ -225,6 +225,7 @@ INSTALLED_APPS = [
|
||||
'pretix.presale',
|
||||
'pretix.multidomain',
|
||||
'pretix.api',
|
||||
'pretix.helpers',
|
||||
'rest_framework',
|
||||
'django_filters',
|
||||
'compressor',
|
||||
@@ -239,7 +240,6 @@ INSTALLED_APPS = [
|
||||
'pretix.plugins.reports',
|
||||
'pretix.plugins.checkinlists',
|
||||
'pretix.plugins.pretixdroid',
|
||||
'easy_thumbnails',
|
||||
'django_markup',
|
||||
'django_otp',
|
||||
'django_otp.plugins.otp_totp',
|
||||
@@ -463,13 +463,6 @@ MESSAGE_TAGS = {
|
||||
}
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
|
||||
|
||||
THUMBNAIL_ALIASES = {
|
||||
'': {
|
||||
'productlist': {'size': (60, 60), 'crop': True},
|
||||
'logo': {'size': (5000, 120), 'crop': False},
|
||||
},
|
||||
}
|
||||
|
||||
loglevel = 'DEBUG' if DEBUG else 'INFO'
|
||||
|
||||
LOGGING = {
|
||||
|
||||
@@ -10,7 +10,7 @@ django-hierarkey==1.0.*,>=1.0.3
|
||||
django-filter==1.0.*
|
||||
reportlab==3.4.*
|
||||
PyPDF2==1.26.*
|
||||
easy-thumbnails==2.4.*
|
||||
Pillow
|
||||
django-libsass
|
||||
libsass
|
||||
django-otp==0.3.*
|
||||
|
||||
@@ -74,7 +74,7 @@ setup(
|
||||
'django-hierarkey==1.0.*,>=1.0.2',
|
||||
'django-filter==1.0.*',
|
||||
'reportlab==3.4.*',
|
||||
'easy-thumbnails==2.4.*',
|
||||
'Pillow',
|
||||
'PyPDF2==1.26.*',
|
||||
'django-libsass',
|
||||
'libsass',
|
||||
|
||||
Reference in New Issue
Block a user