From 9a8389cd4939eaf8c83303fc5c659cb885e8318c Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 13 Jun 2024 22:03:35 -0400 Subject: [PATCH] note: basic support of image attachments --- catalog/templates/_item_user_pieces.html | 10 +++++++++ journal/migrations/0002_note.py | 2 +- journal/models/common.py | 2 +- journal/models/note.py | 28 +++++++++++++++++++----- journal/models/renderers.py | 14 +++++++++++- mastodon/api.py | 25 ++++++++++++++++++--- 6 files changed, 70 insertions(+), 11 deletions(-) diff --git a/catalog/templates/_item_user_pieces.html b/catalog/templates/_item_user_pieces.html index e961789d..4e03832d 100644 --- a/catalog/templates/_item_user_pieces.html +++ b/catalog/templates/_item_user_pieces.html @@ -92,6 +92,16 @@
{{ note.title|default:'' }}

{{ note.content|linebreaks }}

+
+ {% for attachment in note.attachments %} + {% if attachment.type == 'image' %} + image attachment + {% endif %} + {% endfor %} +
{% endfor %}
diff --git a/journal/migrations/0002_note.py b/journal/migrations/0002_note.py index ef33ee41..da630bf7 100644 --- a/journal/migrations/0002_note.py +++ b/journal/migrations/0002_note.py @@ -41,7 +41,7 @@ class Migration(migrations.Migration): ("title", models.TextField(blank=True, default=None, null=True)), ("content", models.TextField()), ("sensitive", models.BooleanField(default=False)), - ("attachements", models.JSONField(default=list)), + ("attachments", models.JSONField(default=list)), ( "item", models.ForeignKey( diff --git a/journal/models/common.py b/journal/models/common.py index 2e2cde5e..375198c6 100644 --- a/journal/models/common.py +++ b/journal/models/common.py @@ -228,7 +228,7 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin): return pp.post_id if pp else None @cached_property - def latest_post(self): + def latest_post(self) -> "Post | None": pk = self.latest_post_id return Takahe.get_post(pk) if pk else None diff --git a/journal/models/note.py b/journal/models/note.py index 671e9059..cf84837b 100644 --- a/journal/models/note.py +++ b/journal/models/note.py @@ -16,7 +16,7 @@ class Note(Content): title = models.TextField(blank=True, null=True, default=None) content = models.TextField(blank=False, null=False) sensitive = models.BooleanField(default=False, null=False) - attachements = models.JSONField(default=list) + attachments = models.JSONField(default=list) @property def html(self): @@ -41,12 +41,23 @@ class Note(Content): @override @classmethod def params_from_ap_object(cls, post, obj, piece): - return { + params = { "title": obj.get("title", post.summary), "content": obj.get("content", "").strip(), "sensitive": obj.get("sensitive", post.sensitive), - # "attachements": obj.get("attachements", []), + "attachments": [], } + if post: + for atta in post.attachments.all(): + params["attachments"].append( + { + "type": (atta.mimetype or "unknown").split("/")[0], + "mimetype": atta.mimetype, + "url": atta.full_url().absolute, + "preview_url": atta.thumbnail_url().absolute, + } + ) + return params @override @classmethod @@ -66,15 +77,21 @@ class Note(Content): return ShelfMember.objects.filter(item=self.item, owner=self.owner).first() def to_mastodon_params(self): - return { + params = { "spoiler_text": self.title, "content": self.content, "sensitive": self.sensitive, - # "attachements": self.attachements, "reply_to_toot_url": ( self.shelfmember.get_mastodon_repost_url() if self.shelfmember else None ), } + if self.latest_post: + attachments = [] + for atta in self.latest_post.attachments.all(): + attachments.append((atta.file_display_name, atta.file, atta.mimetype)) + if attachments: + params["attachments"] = attachments + return params def to_post_params(self): return { @@ -84,4 +101,5 @@ class Note(Content): "reply_to_pk": ( self.shelfmember.latest_post_id if self.shelfmember else None ), + # not passing "attachments" so it won't change } diff --git a/journal/models/renderers.py b/journal/models/renderers.py index d6beffbb..f708432a 100644 --- a/journal/models/renderers.py +++ b/journal/models/renderers.py @@ -1,9 +1,13 @@ import re -from typing import cast +from typing import TYPE_CHECKING, cast import mistune from django.utils.html import escape +if TYPE_CHECKING: + from catalog.models import Item + from users.models import User + _mistune_plugins = [ "url", "strikethrough", @@ -50,3 +54,11 @@ def _spolier(s: str) -> str: def render_text(s: str) -> str: return _spolier(s).strip().replace("\n", "
") + + +def render_post_with_macro(txt: str, item: "Item") -> str: + return ( + txt.replace("[category]", item.category.name) + .replace("[title]", item.display_title) + .replace("[url]", item.absolute_url) + ) diff --git a/mastodon/api.py b/mastodon/api.py index 4fdede0a..c72d3cd4 100644 --- a/mastodon/api.py +++ b/mastodon/api.py @@ -262,14 +262,16 @@ def post_toot2( reply_to_toot_url: str | None = None, sensitive: bool = False, spoiler_text: str | None = None, + attachments: list = [], ): headers = { "User-Agent": USER_AGENT, "Authorization": f"Bearer {user.mastodon_token}", "Idempotency-Key": random_string_generator(16), } + base_url = "https://" + get_api_domain(user.mastodon_site) response = None - url = "https://" + get_api_domain(user.mastodon_site) + API_PUBLISH_TOOT + url = base_url + API_PUBLISH_TOOT payload = { "status": content, "visibility": get_toot_visibility(visibility, user), @@ -278,12 +280,29 @@ def post_toot2( reply_to_id = get_status_id_by_url(reply_to_toot_url) if reply_to_id: payload["in_reply_to_id"] = reply_to_id - # if media_id: - # payload["media_ids[]"] = [media_id] if spoiler_text: payload["spoiler_text"] = spoiler_text if sensitive: payload["sensitive"] = True + media_ids = [] + for atta in attachments: + try: + media_id = ( + requests.post( + base_url + "/api/v1/media", + headers=headers, + data={}, + files={"file": atta}, + ) + .json() + .get("id") + ) + media_ids.append(media_id) + except Exception as e: + logger.warning(f"Error uploading image {e}") + headers["Idempotency-Key"] = random_string_generator(16) + if media_ids: + payload["media_ids[]"] = media_ids try: if update_id: response = put(url + "/" + update_id, headers=headers, data=payload)