First
[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:player",
80                    content: MediaProxy.url(url["href"])
81                  ], []}
82                 | acc
83               ]
84               |> maybe_add_dimensions(url)
85
86             "video" ->
87               # fallback to old placeholder values
88               height = url["height"] || 480
89               width = url["width"] || 480
90
91               [
92                 {:meta, [name: "twitter:card", content: "player"], []},
93                 {:meta, [name: "twitter:player", content: player_url(id)], []},
94                 {:meta, [name: "twitter:player:width", content: "#{width}"], []},
95                 {:meta, [name: "twitter:player:height", content: "#{height}"], []},
96                 {:meta, [name: "twitter:player:stream", content: MediaProxy.url(url["href"])],
97                  []},
98                 {:meta, [name: "twitter:player:stream:content_type", content: url["mediaType"]],
99                  []}
100                 | acc
101               ]
102
103             _ ->
104               acc
105           end
106         end)
107
108       acc ++ rendered_tags
109     end)
110   end
111
112   defp build_attachments(_id, _object), do: []
113
114   defp player_url(id) do
115     Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
116   end
117
118   # Videos have problems without dimensions, but we used to not provide WxH for images.
119   # A default (read: incorrect) fallback for images is likely to cause rendering bugs.
120   defp maybe_add_dimensions(metadata, url) do
121     cond do
122       !is_nil(url["height"]) && !is_nil(url["width"]) ->
123         metadata ++
124           [
125             {:meta, [name: "twitter:player:width", content: "#{url["width"]}"], []},
126             {:meta, [name: "twitter:player:height", content: "#{url["height"]}"], []}
127           ]
128
129       true ->
130         metadata
131     end
132   end
133 end