diff options
Diffstat (limited to 'activities/models')
| -rw-r--r-- | activities/models/__init__.py | 1 | ||||
| -rw-r--r-- | activities/models/post.py | 42 | ||||
| -rw-r--r-- | activities/models/post_attachment.py | 55 | 
3 files changed, 91 insertions, 7 deletions
diff --git a/activities/models/__init__.py b/activities/models/__init__.py index 48ba879..1ae3f4c 100644 --- a/activities/models/__init__.py +++ b/activities/models/__init__.py @@ -1,4 +1,5 @@  from .fan_out import FanOut, FanOutStates  # noqa  from .post import Post, PostStates  # noqa +from .post_attachment import PostAttachment, PostAttachmentStates  # noqa  from .post_interaction import PostInteraction, PostInteractionStates  # noqa  from .timeline_event import TimelineEvent  # noqa diff --git a/activities/models/post.py b/activities/models/post.py index 473755b..caa2981 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -146,6 +146,9 @@ class Post(StatorModel):      def __str__(self):          return f"{self.author} #{self.id}" +    def get_absolute_url(self): +        return self.urls.view +      @property      def safe_content(self):          return sanitize_post(self.content) @@ -244,11 +247,12 @@ class Post(StatorModel):                  raise KeyError(f"No post with ID {data['id']}", data)          if update or created:              post.content = sanitize_post(data["content"]) -            post.summary = data.get("summary", None) +            post.summary = data.get("summary")              post.sensitive = data.get("as:sensitive", False) -            post.url = data.get("url", None) -            post.published = parse_ld_date(data.get("published", None)) -            post.edited = parse_ld_date(data.get("updated", None)) +            post.url = data.get("url") +            post.published = parse_ld_date(data.get("published")) +            post.edited = parse_ld_date(data.get("updated")) +            post.in_reply_to = data.get("inReplyTo")              # Mentions and hashtags              post.hashtags = []              for tag in get_list(data, "tag"): @@ -270,6 +274,26 @@ class Post(StatorModel):              for target in targets:                  if target.lower() == "as:public":                      post.visibility = Post.Visibilities.public +            # Attachments +            # These have no IDs, so we have to wipe them each time +            post.attachments.all().delete() +            for attachment in get_list(data, "attachment"): +                if "http://joinmastodon.org/ns#focalPoint" in attachment: +                    focal_x, focal_y = attachment[ +                        "http://joinmastodon.org/ns#focalPoint" +                    ]["@list"] +                else: +                    focal_x, focal_y = None, None +                post.attachments.create( +                    remote_url=attachment["url"], +                    mimetype=attachment["mediaType"], +                    name=attachment.get("name"), +                    width=attachment.get("width"), +                    height=attachment.get("height"), +                    blurhash=attachment.get("http://joinmastodon.org/ns#blurhash"), +                    focal_x=focal_x, +                    focal_y=focal_y, +                )              post.save()          return post @@ -308,9 +332,13 @@ class Post(StatorModel):                  raise ValueError("Create actor does not match its Post object", data)              # Create it              post = cls.by_ap(data["object"], create=True, update=True) -            # Make timeline events for followers -            for follow in Follow.objects.filter(target=post.author, source__local=True): -                TimelineEvent.add_post(follow.source, post) +            # Make timeline events for followers if it's not a reply +            # TODO: _do_ show replies to people we follow somehow +            if not post.in_reply_to: +                for follow in Follow.objects.filter( +                    target=post.author, source__local=True +                ): +                    TimelineEvent.add_post(follow.source, post)              # Make timeline events for mentions if they're local              for mention in post.mentions.all():                  if mention.local: diff --git a/activities/models/post_attachment.py b/activities/models/post_attachment.py new file mode 100644 index 0000000..ee77d29 --- /dev/null +++ b/activities/models/post_attachment.py @@ -0,0 +1,55 @@ +from django.db import models + +from stator.models import State, StateField, StateGraph, StatorModel + + +class PostAttachmentStates(StateGraph): +    new = State(try_interval=30000) +    fetched = State() + +    new.transitions_to(fetched) + +    @classmethod +    async def handle_new(cls, instance): +        # TODO: Fetch images to our own media storage +        pass + + +class PostAttachment(StatorModel): +    """ +    An attachment to a Post. Could be an image, a video, etc. +    """ + +    post = models.ForeignKey( +        "activities.post", +        on_delete=models.CASCADE, +        related_name="attachments", +    ) + +    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 +    file = models.FileField(upload_to="attachments/%Y/%m/%d/", null=True, blank=True) + +    remote_url = models.CharField(max_length=500, null=True, blank=True) + +    # This is the description for images, at least +    name = models.TextField(null=True, blank=True) + +    width = models.IntegerField(null=True, blank=True) +    height = models.IntegerField(null=True, blank=True) +    focal_x = models.IntegerField(null=True, blank=True) +    focal_y = models.IntegerField(null=True, blank=True) +    blurhash = models.TextField(null=True, blank=True) + +    def is_image(self): +        return self.mimetype in [ +            "image/apng", +            "image/avif", +            "image/gif", +            "image/jpeg", +            "image/png", +            "image/webp", +        ]  | 
