diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 01dfbba7..aa9a5601 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -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) diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index 2c5a2164..dc226bce 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -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() diff --git a/bookwyrm/preview_images.py b/bookwyrm/preview_images.py index 82e8dc79..949ffac1 100644 --- a/bookwyrm/preview_images.py +++ b/bookwyrm/preview_images.py @@ -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)