Add site preview task

This commit is contained in:
Joachim 2021-05-26 10:19:39 +02:00
parent 101ca0ff81
commit 34caa36ab7
3 changed files with 79 additions and 16 deletions

View file

@ -86,7 +86,7 @@ class Book(BookDataModel):
upload_to="covers/", blank=True, null=True, alt_field="alt_text"
)
preview_image = models.ImageField(
upload_to="cover_previews/", blank=True, null=True
upload_to="previews/covers/", blank=True, null=True
)
first_published_date = fields.DateTimeField(blank=True, null=True)
published_date = fields.DateTimeField(blank=True, null=True)

View file

@ -4,9 +4,12 @@ import datetime
from Crypto import Random
from django.db import models, IntegrityError
from django.dispatch import receiver
from django.utils import timezone
from bookwyrm.preview_images import generate_site_preview_image_task
from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app
from .base_model import BookWyrmModel
from .user import User
@ -35,6 +38,7 @@ class SiteSettings(models.Model):
logo = models.ImageField(upload_to="logos/", null=True, blank=True)
logo_small = models.ImageField(upload_to="logos/", null=True, blank=True)
favicon = models.ImageField(upload_to="logos/", null=True, blank=True)
preview_image = models.ImageField(upload_to="previews/logos/", null=True, blank=True)
# footer
support_link = models.CharField(max_length=255, null=True, blank=True)
@ -119,3 +123,12 @@ class PasswordReset(models.Model):
def link(self):
"""formats the invite link"""
return "https://{}/password-reset/{}".format(DOMAIN, self.code)
@receiver(models.signals.post_save, sender=SiteSettings)
# pylint: disable=unused-argument
def preview_image(instance, *args, **kwargs):
updated_fields = kwargs["update_fields"]
if not updated_fields or "preview_image" not in updated_fields:
generate_site_preview_image_task.delay()

View file

@ -14,6 +14,7 @@ from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db.models import Avg
from bookwyrm import models, settings
from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app
# dev
@ -182,7 +183,7 @@ def generate_default_cover():
return default_cover
def generate_preview_image(book, texts={}, picture=None, rating=None):
def generate_preview_image(texts={}, picture=None, rating=None, show_instance_layer=True):
# Cover
try:
cover_img_layer = Image.open(picture)
@ -217,31 +218,35 @@ def generate_preview_image(book, texts={}, picture=None, rating=None):
content_x = margin + cover_img_layer.width + gutter
content_width = IMG_WIDTH - content_x - margin
instance_layer = generate_instance_layer(content_width)
texts_layer = generate_texts_layer(texts, content_width)
contents_layer = Image.new(
"RGBA", (content_width, IMG_HEIGHT), color=TRANSPARENT_COLOR
)
contents_composite_y = 0
contents_layer.alpha_composite(instance_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + instance_layer.height + gutter
if show_instance_layer:
instance_layer = generate_instance_layer(content_width)
contents_layer.alpha_composite(instance_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + instance_layer.height + gutter
texts_layer = generate_texts_layer(texts, content_width)
contents_layer.alpha_composite(texts_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + texts_layer.height + 30
contents_composite_y = contents_composite_y + texts_layer.height + gutter
if rating:
# Add some more margin
contents_composite_y = contents_composite_y + 30
contents_composite_y = contents_composite_y + gutter
rating_layer = generate_rating_layer(rating, content_width)
contents_layer.alpha_composite(rating_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + rating_layer.height + 30
contents_composite_y = contents_composite_y + rating_layer.height + gutter
contents_layer_box = contents_layer.getbbox()
contents_layer_height = contents_layer_box[3] - contents_layer_box[1]
contents_y = math.floor((IMG_HEIGHT - contents_layer_height) / 2)
# Remove Instance Layer from centering calculations
contents_y = contents_y - math.floor((instance_layer.height + gutter) / 2)
if show_instance_layer:
# Remove Instance Layer from centering calculations
contents_y = contents_y - math.floor((instance_layer.height + gutter) / 2)
if contents_y < margin:
contents_y = margin
@ -255,9 +260,56 @@ def generate_preview_image(book, texts={}, picture=None, rating=None):
return img
@app.task
def generate_site_preview_image_task():
"""generate preview_image for the website"""
site = models.SiteSettings.objects.get()
if site.logo:
logo = site.logo
else:
logo = path.joinpath("static/images/logo-small.png")
texts = {
'text_zero': DOMAIN,
'text_one': site.name,
'text_three': site.instance_tagline,
}
img = generate_preview_image(texts=texts,
picture=logo,
show_instance_layer=False)
file_name = "%s.png" % str(uuid4())
image_buffer = BytesIO()
try:
try:
old_path = site.preview_image.path
except ValueError:
old_path = ""
# Save
img.save(image_buffer, format="png")
site.preview_image = InMemoryUploadedFile(
ContentFile(image_buffer.getvalue()),
"preview_image",
file_name,
"image/png",
image_buffer.tell(),
None,
)
site.save(update_fields=["preview_image"])
# Clean up old file after saving
if os.path.exists(old_path):
os.remove(old_path)
finally:
image_buffer.close()
@app.task
def generate_edition_preview_image_task(book_id):
"""generate preview_image"""
"""generate preview_image for a book"""
book = models.Book.objects.select_subclasses().get(id=book_id)
rating = models.Review.objects.filter(
@ -267,14 +319,12 @@ def generate_edition_preview_image_task(book_id):
).aggregate(Avg("rating"))["rating__avg"]
texts = {
'text_zero': "ADDED A REVIEW",
'text_one': book.title,
'text_two': book.subtitle,
'text_three': book.author_text
}
img = generate_preview_image(book=book,
texts=texts,
img = generate_preview_image(texts=texts,
picture=book.cover,
rating=rating)