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" upload_to="covers/", blank=True, null=True, alt_field="alt_text"
) )
preview_image = models.ImageField( 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) first_published_date = fields.DateTimeField(blank=True, null=True)
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 Crypto import Random
from django.db import models, IntegrityError from django.db import models, IntegrityError
from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from bookwyrm.preview_images import generate_site_preview_image_task
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from .user import User from .user import User
@ -35,6 +38,7 @@ class SiteSettings(models.Model):
logo = models.ImageField(upload_to="logos/", null=True, blank=True) logo = models.ImageField(upload_to="logos/", null=True, blank=True)
logo_small = 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) favicon = models.ImageField(upload_to="logos/", null=True, blank=True)
preview_image = models.ImageField(upload_to="previews/logos/", null=True, blank=True)
# footer # footer
support_link = models.CharField(max_length=255, null=True, blank=True) support_link = models.CharField(max_length=255, null=True, blank=True)
@ -119,3 +123,12 @@ class PasswordReset(models.Model):
def link(self): def link(self):
"""formats the invite link""" """formats the invite link"""
return "https://{}/password-reset/{}".format(DOMAIN, self.code) 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 django.db.models import Avg
from bookwyrm import models, settings from bookwyrm import models, settings
from bookwyrm.settings import DOMAIN
from bookwyrm.tasks import app from bookwyrm.tasks import app
# dev # dev
@ -182,7 +183,7 @@ def generate_default_cover():
return 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 # Cover
try: try:
cover_img_layer = Image.open(picture) cover_img_layer = Image.open(picture)
@ -217,29 +218,33 @@ def generate_preview_image(book, texts={}, picture=None, rating=None):
content_x = margin + cover_img_layer.width + gutter content_x = margin + cover_img_layer.width + gutter
content_width = IMG_WIDTH - content_x - margin 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( contents_layer = Image.new(
"RGBA", (content_width, IMG_HEIGHT), color=TRANSPARENT_COLOR "RGBA", (content_width, IMG_HEIGHT), color=TRANSPARENT_COLOR
) )
contents_composite_y = 0 contents_composite_y = 0
if show_instance_layer:
instance_layer = generate_instance_layer(content_width)
contents_layer.alpha_composite(instance_layer, (0, contents_composite_y)) contents_layer.alpha_composite(instance_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + instance_layer.height + gutter 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_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: if rating:
# Add some more margin # 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) rating_layer = generate_rating_layer(rating, content_width)
contents_layer.alpha_composite(rating_layer, (0, contents_composite_y)) 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_box = contents_layer.getbbox()
contents_layer_height = contents_layer_box[3] - contents_layer_box[1] contents_layer_height = contents_layer_box[3] - contents_layer_box[1]
contents_y = math.floor((IMG_HEIGHT - contents_layer_height) / 2) contents_y = math.floor((IMG_HEIGHT - contents_layer_height) / 2)
if show_instance_layer:
# Remove Instance Layer from centering calculations # Remove Instance Layer from centering calculations
contents_y = contents_y - math.floor((instance_layer.height + gutter) / 2) contents_y = contents_y - math.floor((instance_layer.height + gutter) / 2)
@ -255,9 +260,56 @@ def generate_preview_image(book, texts={}, picture=None, rating=None):
return img 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 @app.task
def generate_edition_preview_image_task(book_id): 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) book = models.Book.objects.select_subclasses().get(id=book_id)
rating = models.Review.objects.filter( rating = models.Review.objects.filter(
@ -267,14 +319,12 @@ def generate_edition_preview_image_task(book_id):
).aggregate(Avg("rating"))["rating__avg"] ).aggregate(Avg("rating"))["rating__avg"]
texts = { texts = {
'text_zero': "ADDED A REVIEW",
'text_one': book.title, 'text_one': book.title,
'text_two': book.subtitle, 'text_two': book.subtitle,
'text_three': book.author_text 'text_three': book.author_text
} }
img = generate_preview_image(book=book, img = generate_preview_image(texts=texts,
texts=texts,
picture=book.cover, picture=book.cover,
rating=rating) rating=rating)