From 6f2f28a3a752cc47d9dc96bda862ed67cd75c9af Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Thu, 1 Dec 2022 18:46:49 -0700 Subject: Image attachment uploads --- activities/models/post.py | 22 ++++++++++++------ activities/models/post_attachment.py | 43 ++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 9 deletions(-) (limited to 'activities/models') diff --git a/activities/models/post.py b/activities/models/post.py index eecce04..d3365e7 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -1,5 +1,5 @@ import re -from typing import Dict, Iterable, Optional, Set +from typing import Dict, Iterable, List, Optional, Set import httpx import urlman @@ -312,7 +312,7 @@ class Post(StatorModel): """ return ( await Post.objects.select_related("author", "author__domain") - .prefetch_related("mentions", "mentions__domain") + .prefetch_related("mentions", "mentions__domain", "attachments") .aget(pk=self.pk) ) @@ -326,6 +326,7 @@ class Post(StatorModel): summary: Optional[str] = None, visibility: int = Visibilities.public, reply_to: Optional["Post"] = None, + attachments: Optional[List] = None, ) -> "Post": with transaction.atomic(): # Find mentions in this post @@ -353,6 +354,8 @@ class Post(StatorModel): post.object_uri = post.urls.object_uri post.url = post.absolute_object_uri() post.mentions.set(mentions) + if attachments: + post.attachments.set(attachments) post.save() return post @@ -361,6 +364,7 @@ class Post(StatorModel): content: str, summary: Optional[str] = None, visibility: int = Visibilities.public, + attachments: Optional[List] = None, ): with transaction.atomic(): # Strip all HTML and apply linebreaks filter @@ -371,6 +375,7 @@ class Post(StatorModel): self.edited = timezone.now() self.hashtags = Hashtag.hashtags_from_content(content) or None self.mentions.set(self.mentions_from_content(content, self.author)) + self.attachments.set(attachments or []) self.save() @classmethod @@ -421,6 +426,7 @@ class Post(StatorModel): "as:sensitive": self.sensitive, "url": self.absolute_object_uri(), "tag": [], + "attachment": [], } if self.summary: value["summary"] = self.summary @@ -438,11 +444,13 @@ class Post(StatorModel): } ) value["cc"].append(mention.actor_uri) - # Remove tag and cc if they're empty - if not value["cc"]: - del value["cc"] - if not value["tag"]: - del value["tag"] + # Attachments + for attachment in self.attachments.all(): + value["attachment"].append(attachment.to_ap()) + # Remove fields if they're empty + for field in ["cc", "tag", "attachment"]: + if not value[field]: + del value[field] return value def to_create_ap(self): diff --git a/activities/models/post_attachment.py b/activities/models/post_attachment.py index 6ccea08..7feaba5 100644 --- a/activities/models/post_attachment.py +++ b/activities/models/post_attachment.py @@ -27,15 +27,24 @@ class PostAttachment(StatorModel): "activities.post", on_delete=models.CASCADE, related_name="attachments", + blank=True, + null=True, ) state = StateField(graph=PostAttachmentStates) mimetype = models.CharField(max_length=200) - # File may not be populated if it's remote and not cached on our side yet + # Files may not be populated if it's remote and not cached on our side yet file = models.FileField( - upload_to=partial(upload_namer, "attachments"), null=True, blank=True + upload_to=partial(upload_namer, "attachments"), + null=True, + blank=True, + ) + thumbnail = models.ImageField( + upload_to=partial(upload_namer, "attachment_thumbnails"), + null=True, + blank=True, ) remote_url = models.CharField(max_length=500, null=True, blank=True) @@ -49,6 +58,9 @@ class PostAttachment(StatorModel): focal_y = models.IntegerField(null=True, blank=True) blurhash = models.TextField(null=True, blank=True) + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + def is_image(self): return self.mimetype in [ "image/apng", @@ -58,3 +70,30 @@ class PostAttachment(StatorModel): "image/png", "image/webp", ] + + def thumbnail_url(self): + if self.thumbnail: + return self.thumbnail.url + elif self.file: + return self.file.url + else: + return self.remote_url + + def full_url(self): + if self.file: + return self.file.url + else: + return self.remote_url + + ### ActivityPub ### + + def to_ap(self): + return { + "url": self.file.url, + "name": self.name, + "type": "Document", + "width": self.width, + "height": self.height, + "mediaType": self.mimetype, + "http://joinmastodon.org/ns#focalPoint": [0.5, 0.5], + } -- cgit v1.2.3