Repository: bookwyrm-social/bookwyrm

Issue:
Cover images when placing a bookwyrm url into a mastodon post
I don't know if this is a bug or its a feature thats needed but I'd notice when posting to mastodon and including the bookwyrm URL for the book  I am discussing that rather than embedding a book cover image it just includes a giant bookwyrm advert. I'd certainly prefer it to embed a cover image.


Discussion:
I believe that when a preview image is not available for a book, the template defaults back to the instance logo, which I'm guessing is what you mean by an advert. It would be a quick fix to use a book cover as a fallback on the book page for books with no generated preview image.
I mean like this.

![SCR-20230103-fgc](https://user-images.githubusercontent.com/7078397/210286298-d3dcc3a7-4176-45c8-a3fc-aed9a1ee9308.jpeg)

The bookwyrm page does have a cover image, and its one that appears in my feed, just that when I post the link to the book in mastodon for some reason its not displayed.
Yes, I see what you mean. Right now this is what it's doing:
1. if there's a book preview image, use that
2. if there's an instance preview image (that's what's in your screen shot), use that
3. otherwise use the instance logo

Instead, it should do:
1. if there's a book preview image, use that
2. **if there's a book cover, use that**
3. if there's an instance preview image (that's what's in your screen shot), use that
4. otherwise use the instance logo

Resolving patch:
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -12,6 +12,8 @@
 from django.dispatch import receiver
 from django.template.loader import get_template
 from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+from django.utils.translation import ngettext_lazy
 from model_utils import FieldTracker
 from model_utils.managers import InheritanceManager
 
@@ -178,6 +180,24 @@ def boostable(self):
         """you can't boost dms"""
         return self.privacy in ["unlisted", "public"]
 
+    @property
+    def page_title(self):
+        """title of the page when only this status is shown"""
+        return _("%(display_name)s's status") % {"display_name": self.user.display_name}
+
+    @property
+    def page_description(self):
+        """description of the page in meta tags when only this status is shown"""
+        return None
+
+    @property
+    def page_image(self):
+        """image to use as preview in meta tags when only this status is shown"""
+        if self.mention_books.exists():
+            book = self.mention_books.first()
+            return book.preview_image or book.cover
+        return self.user.preview_image
+
     def to_replies(self, **kwargs):
         """helper function for loading AP serialized replies to a status"""
         return self.to_ordered_collection(
@@ -301,6 +321,10 @@ class Meta:
 
         abstract = True
 
+    @property
+    def page_image(self):
+        return self.book.preview_image or self.book.cover or super().page_image
+
 
 class Comment(BookStatus):
     """like a review but without a rating and transient"""
@@ -332,6 +356,13 @@ def pure_content(self):
 
     activity_serializer = activitypub.Comment
 
+    @property
+    def page_title(self):
+        return _("%(display_name)s's comment on %(book_title)s") % {
+            "display_name": self.user.display_name,
+            "book_title": self.book.title,
+        }
+
 
 class Quotation(BookStatus):
     """like a review but without a rating and transient"""
@@ -374,6 +405,13 @@ def pure_content(self):
 
     activity_serializer = activitypub.Quotation
 
+    @property
+    def page_title(self):
+        return _("%(display_name)s's quote from %(book_title)s") % {
+            "display_name": self.user.display_name,
+            "book_title": self.book.title,
+        }
+
 
 class Review(BookStatus):
     """a book review"""
@@ -403,6 +441,13 @@ def pure_content(self):
         """indicate the book in question for mastodon (or w/e) users"""
         return self.content
 
+    @property
+    def page_title(self):
+        return _("%(display_name)s's review of %(book_title)s") % {
+            "display_name": self.user.display_name,
+            "book_title": self.book.title,
+        }
+
     activity_serializer = activitypub.Review
     pure_type = "Article"
 
@@ -426,6 +471,18 @@ def pure_content(self):
         template = get_template("snippets/generated_status/rating.html")
         return template.render({"book": self.book, "rating": self.rating}).strip()
 
+    @property
+    def page_description(self):
+        return ngettext_lazy(
+            "%(display_name)s rated %(book_title)s: %(display_rating).1f star",
+            "%(display_name)s rated %(book_title)s: %(display_rating).1f stars",
+            "display_rating",
+        ) % {
+            "display_name": self.user.display_name,
+            "book_title": self.book.title,
+            "display_rating": self.rating,
+        }
+
     activity_serializer = activitypub.Rating
     pure_type = "Note"
 
diff --git a/bookwyrm/views/feed.py b/bookwyrm/views/feed.py
--- a/bookwyrm/views/feed.py
+++ b/bookwyrm/views/feed.py
@@ -185,19 +185,15 @@ def get(self, request, username, status_id, slug=None):
             params=[status.id, visible_thread, visible_thread],
         )
 
-        preview = None
-        if hasattr(status, "book"):
-            preview = status.book.preview_image
-        elif status.mention_books.exists():
-            preview = status.mention_books.first().preview_image
-
         data = {
             **feed_page_data(request.user),
             **{
                 "status": status,
                 "children": children,
                 "ancestors": ancestors,
-                "preview": preview,
+                "title": status.page_title,
+                "description": status.page_description,
+                "page_image": status.page_image,
             },
         }
         return TemplateResponse(request, "feed/status.html", data)


Test patch: