total rebase
[anni] / lib / pleroma / web / metadata / providers / twitter_card.ex
1 # Pleroma: A lightweight social networking server
2
3 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
4 # SPDX-License-Identifier: AGPL-3.0-only
5
6 defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
7   alias Pleroma.User
8   alias Pleroma.Web.MediaProxy
9   alias Pleroma.Web.Metadata
10   alias Pleroma.Web.Metadata.Providers.Provider
11   alias Pleroma.Web.Metadata.Utils
12
13   @behaviour Provider
14   @media_types ["image", "audio", "video"]
15
16   @impl Provider
17   def build_tags(%{activity_id: id, object: object, user: user}) do
18     attachments = build_attachments(id, object)
19     scrubbed_content = Utils.scrub_html_and_truncate(object)
20
21     [
22       title_tag(user),
23       {:meta, [name: "twitter:description", content: scrubbed_content], []}
24     ] ++
25       if attachments == [] or Metadata.activity_nsfw?(object) do
26         [
27           image_tag(user),
28           {:meta, [name: "twitter:card", content: "summary"], []}
29         ]
30       else
31         attachments
32       end
33   end
34
35   @impl Provider
36   def build_tags(%{user: user}) do
37     with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
38       [
39         title_tag(user),
40         {:meta, [name: "twitter:description", content: truncated_bio], []},
41         image_tag(user),
42         {:meta, [name: "twitter:card", content: "summary"], []}
43       ]
44     end
45   end
46
47   defp title_tag(user) do
48     {:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []}
49   end
50
51   def image_tag(user) do
52     {:meta, [name: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], []}
53   end
54
55   defp build_attachments(id, %{data: %{"attachment" => attachments}}) do
56     Enum.reduce(attachments, [], fn attachment, acc ->
57       rendered_tags =
58         Enum.reduce(attachment["url"], [], fn url, acc ->
59           case Utils.fetch_media_type(@media_types, url["mediaType"]) do
60             "audio" ->
61               [
62                 {:meta, [name: "twitter:card", content: "player"], []},
63                 {:meta, [name: "twitter:player:width", content: "480"], []},
64                 {:meta, [name: "twitter:player:height", content: "80"], []},
65                 {:meta, [name: "twitter:player", content: player_url(id)], []}
66                 | acc
67               ]
68
69             # Not using preview_url for this. It saves bandwidth, but the image dimensions will
70             # be wrong. We generate it on the fly and have no way to capture or analyze the
71             # image to get the dimensions. This can be an issue for apps/FEs rendering images
72             # in timelines too, but you can get clever with the aspect ratio metadata as a
73             # workaround.
74             "image" ->
75               [
76                 {:meta, [name: "twitter:card", content: "summary_large_image"], []},
77                 {:meta,
78                  [
79                    name: "twitter:image",
80                    content: MediaProxy.url(url["href"])
81                  ], []},
82                 {:meta, [name: "twitter:image:alt", content: truncate(attachment["name"])], []}
83                 | acc
84               ]
85               |> maybe_add_dimensions(url)
86
87             "video" ->
88               # fallback to old placeholder values
89               height = url["height"] || 480
90               width = url["width"] || 480
91
92               [
93                 {:meta, [name: "twitter:card", content: "player"], []},
94                 {:meta, [name: "twitter:player", content: player_url(id)], []},
95                 {:meta, [name: "twitter:player:width", content: "#{width}"], []},
96                 {:meta, [name: "twitter:player:height", content: "#{height}"], []},
97                 {:meta, [name: "twitter:player:stream", content: MediaProxy.url(url["href"])],
98                  []},
99                 {:meta, [name: "twitter:player:stream:content_type", content: url["mediaType"]],
100                  []}
101                 | acc
102               ]
103
104             _ ->
105               acc
106           end
107         end)
108
109       acc ++ rendered_tags
110     end)
111   end
112
113   defp build_attachments(_id, _object), do: []
114
115   defp player_url(id) do
116     Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
117   end
118
119   # Videos have problems without dimensions, but we used to not provide WxH for images.
120   # A default (read: incorrect) fallback for images is likely to cause rendering bugs.
121   defp maybe_add_dimensions(metadata, url) do
122     cond do
123       !is_nil(url["height"]) && !is_nil(url["width"]) ->
124         metadata ++
125           [
126             {:meta, [name: "twitter:player:width", content: "#{url["width"]}"], []},
127             {:meta, [name: "twitter:player:height", content: "#{url["height"]}"], []}
128           ]
129
130       true ->
131         metadata
132     end
133   end
134
135   defp truncate(nil), do: ""
136
137   defp truncate(text) do
138     # truncate to 420 characters
139     # see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
140     Pleroma.Formatter.truncate(text, 420)
141   end
142 end