summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfeld <feld@feld.me>2021-06-10 15:03:58 +0000
committerfeld <feld@feld.me>2021-06-10 15:03:58 +0000
commit406dadb56b0f2d5102998a423286dd4c10c877c5 (patch)
treee560fd06dab8306ececd4b5808de9b62b31d6170
parentb84873d3df729f76d0571ff7a0df78412b95959c (diff)
parent202ee5fd77e721c8822dd779c8b558ec8cfacfcc (diff)
Merge branch 'fix/twittercard-video-dimensions' into 'develop'
Generate Twittercard/OGP metadata with correct dimensions See merge request pleroma/pleroma!3456
-rw-r--r--CHANGELOG.md1
-rw-r--r--lib/pleroma/web/metadata/providers/open_graph.ex61
-rw-r--r--lib/pleroma/web/metadata/providers/twitter_card.ex42
-rw-r--r--lib/pleroma/web/metadata/utils.ex5
-rw-r--r--test/pleroma/web/metadata/providers/open_graph_test.exs100
-rw-r--r--test/pleroma/web/metadata/providers/twitter_card_test.exs25
6 files changed, 204 insertions, 30 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dcb462f07..52d92c6d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
- Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
+- Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.
### Added
diff --git a/lib/pleroma/web/metadata/providers/open_graph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex
index 18ddde84b..df0cca74a 100644
--- a/lib/pleroma/web/metadata/providers/open_graph.ex
+++ b/lib/pleroma/web/metadata/providers/open_graph.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
alias Pleroma.User
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata
alias Pleroma.Web.Metadata.Providers.Provider
alias Pleroma.Web.Metadata.Utils
@@ -32,11 +33,11 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
property: "og:description",
content: scrubbed_content
], []},
- {:meta, [property: "og:type", content: "website"], []}
+ {:meta, [property: "og:type", content: "article"], []}
] ++
if attachments == [] or Metadata.activity_nsfw?(object) do
[
- {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))],
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
[]},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
@@ -57,8 +58,9 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
], []},
{:meta, [property: "og:url", content: user.uri || user.ap_id], []},
{:meta, [property: "og:description", content: truncated_bio], []},
- {:meta, [property: "og:type", content: "website"], []},
- {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))], []},
+ {:meta, [property: "og:type", content: "article"], []},
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
+ []},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
]
@@ -69,28 +71,35 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
Enum.reduce(attachments, [], fn attachment, acc ->
rendered_tags =
Enum.reduce(attachment["url"], [], fn url, acc ->
- # TODO: Add additional properties to objects when we have the data available.
- # Also, Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
+ # TODO: Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
# object when a Video or GIF is attached it will display that in Whatsapp Rich Preview.
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
"audio" ->
[
- {:meta, [property: "og:audio", content: Utils.attachment_url(url["href"])], []}
+ {:meta, [property: "og:audio", content: MediaProxy.url(url["href"])], []}
| acc
]
+ # Not using preview_url for this. It saves bandwidth, but the image dimensions will
+ # be wrong. We generate it on the fly and have no way to capture or analyze the
+ # image to get the dimensions. This can be an issue for apps/FEs rendering images
+ # in timelines too, but you can get clever with the aspect ratio metadata as a
+ # workaround.
"image" ->
[
- {:meta, [property: "og:image", content: Utils.attachment_url(url["href"])], []},
+ {:meta, [property: "og:image", content: MediaProxy.url(url["href"])], []},
{:meta, [property: "og:image:alt", content: attachment["name"]], []}
| acc
]
+ |> maybe_add_dimensions(url)
"video" ->
[
- {:meta, [property: "og:video", content: Utils.attachment_url(url["href"])], []}
+ {:meta, [property: "og:video", content: MediaProxy.url(url["href"])], []}
| acc
]
+ |> maybe_add_dimensions(url)
+ |> maybe_add_video_thumbnail(url)
_ ->
acc
@@ -102,4 +111,38 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
end
defp build_attachments(_), do: []
+
+ # We can use url["mediaType"] to dynamically fill the metadata
+ defp maybe_add_dimensions(metadata, url) do
+ type = url["mediaType"] |> String.split("/") |> List.first()
+
+ cond do
+ !is_nil(url["height"]) && !is_nil(url["width"]) ->
+ metadata ++
+ [
+ {:meta, [property: "og:#{type}:width", content: "#{url["width"]}"], []},
+ {:meta, [property: "og:#{type}:height", content: "#{url["height"]}"], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
+
+ # Media Preview Proxy makes thumbnails of videos without resizing, so we can trust the
+ # width and height of the source video.
+ defp maybe_add_video_thumbnail(metadata, url) do
+ cond do
+ Pleroma.Config.get([:media_preview_proxy, :enabled], false) ->
+ metadata ++
+ [
+ {:meta, [property: "og:image:width", content: "#{url["width"]}"], []},
+ {:meta, [property: "og:image:height", content: "#{url["height"]}"], []},
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(url["href"])], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
end
diff --git a/lib/pleroma/web/metadata/providers/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex
index 12c372d77..79183df86 100644
--- a/lib/pleroma/web/metadata/providers/twitter_card.ex
+++ b/lib/pleroma/web/metadata/providers/twitter_card.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
alias Pleroma.User
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata
alias Pleroma.Web.Metadata.Providers.Provider
alias Pleroma.Web.Metadata.Utils
@@ -48,14 +49,14 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
end
def image_tag(user) do
- {:meta, [property: "twitter:image", content: Utils.attachment_url(User.avatar_url(user))], []}
+ {:meta, [property: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))],
+ []}
end
defp build_attachments(id, %{data: %{"attachment" => attachments}}) do
Enum.reduce(attachments, [], fn attachment, acc ->
rendered_tags =
Enum.reduce(attachment["url"], [], fn url, acc ->
- # TODO: Add additional properties to objects when we have the data available.
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
"audio" ->
[
@@ -66,26 +67,35 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
| acc
]
+ # Not using preview_url for this. It saves bandwidth, but the image dimensions will
+ # be wrong. We generate it on the fly and have no way to capture or analyze the
+ # image to get the dimensions. This can be an issue for apps/FEs rendering images
+ # in timelines too, but you can get clever with the aspect ratio metadata as a
+ # workaround.
"image" ->
[
{:meta, [property: "twitter:card", content: "summary_large_image"], []},
{:meta,
[
property: "twitter:player",
- content: Utils.attachment_url(url["href"])
+ content: MediaProxy.url(url["href"])
], []}
| acc
]
+ |> maybe_add_dimensions(url)
- # TODO: Need the true width and height values here or Twitter renders an iFrame with
- # a bad aspect ratio
"video" ->
+ # fallback to old placeholder values
+ height = url["height"] || 480
+ width = url["width"] || 480
+
[
{:meta, [property: "twitter:card", content: "player"], []},
{:meta, [property: "twitter:player", content: player_url(id)], []},
- {:meta, [property: "twitter:player:width", content: "480"], []},
- {:meta, [property: "twitter:player:height", content: "480"], []},
- {:meta, [property: "twitter:player:stream", content: url["href"]], []},
+ {:meta, [property: "twitter:player:width", content: "#{width}"], []},
+ {:meta, [property: "twitter:player:height", content: "#{height}"], []},
+ {:meta, [property: "twitter:player:stream", content: MediaProxy.url(url["href"])],
+ []},
{:meta,
[property: "twitter:player:stream:content_type", content: url["mediaType"]], []}
| acc
@@ -105,4 +115,20 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
defp player_url(id) do
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
end
+
+ # Videos have problems without dimensions, but we used to not provide WxH for images.
+ # A default (read: incorrect) fallback for images is likely to cause rendering bugs.
+ defp maybe_add_dimensions(metadata, url) do
+ cond do
+ !is_nil(url["height"]) && !is_nil(url["width"]) ->
+ metadata ++
+ [
+ {:meta, [property: "twitter:player:width", content: "#{url["width"]}"], []},
+ {:meta, [property: "twitter:player:height", content: "#{url["height"]}"], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
end
diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex
index bc31d66b9..caca42934 100644
--- a/lib/pleroma/web/metadata/utils.ex
+++ b/lib/pleroma/web/metadata/utils.ex
@@ -7,7 +7,6 @@ defmodule Pleroma.Web.Metadata.Utils do
alias Pleroma.Emoji
alias Pleroma.Formatter
alias Pleroma.HTML
- alias Pleroma.Web.MediaProxy
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
content
@@ -38,10 +37,6 @@ defmodule Pleroma.Web.Metadata.Utils do
def scrub_html(content), do: content
- def attachment_url(url) do
- MediaProxy.preview_url(url)
- end
-
def user_name_string(user) do
"#{user.name} " <>
if user.local do
diff --git a/test/pleroma/web/metadata/providers/open_graph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs
index fc44b3cbd..28ca8839c 100644
--- a/test/pleroma/web/metadata/providers/open_graph_test.exs
+++ b/test/pleroma/web/metadata/providers/open_graph_test.exs
@@ -22,7 +22,12 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
"attachment" => [
%{
"url" => [
- %{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}
+ %{
+ "mediaType" => "image/png",
+ "href" => "https://pleroma.gov/tenshi.png",
+ "height" => 1024,
+ "width" => 1280
+ }
]
},
%{
@@ -35,7 +40,12 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
},
%{
"url" => [
- %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
+ %{
+ "mediaType" => "video/webm",
+ "href" => "https://pleroma.gov/about/juche.webm",
+ "height" => 600,
+ "width" => 800
+ }
]
},
%{
@@ -55,11 +65,15 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
assert Enum.all?(
[
{:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []},
+ {:meta, [property: "og:image:width", content: "1280"], []},
+ {:meta, [property: "og:image:height", content: "1024"], []},
{:meta,
[property: "og:audio", content: "http://www.gnu.org/music/free-software-song.au"],
[]},
{:meta, [property: "og:video", content: "https://pleroma.gov/about/juche.webm"],
- []}
+ []},
+ {:meta, [property: "og:video:width", content: "800"], []},
+ {:meta, [property: "og:video:height", content: "600"], []}
],
fn element -> element in result end
)
@@ -93,4 +107,84 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
refute {:meta, [property: "og:image", content: "https://misskey.microsoft/corndog.png"], []} in result
end
+
+ test "video attachments have image thumbnail with WxH metadata with Preview Proxy enabled" do
+ clear_config([:media_proxy, :enabled], true)
+ clear_config([:media_preview_proxy, :enabled], true)
+ user = insert(:user)
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "test video post",
+ "sensitive" => false,
+ "attachment" => [
+ %{
+ "url" => [
+ %{
+ "mediaType" => "video/webm",
+ "href" => "https://pleroma.gov/about/juche.webm",
+ "height" => 600,
+ "width" => 800
+ }
+ ]
+ }
+ ]
+ }
+ })
+
+ result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
+
+ assert {:meta, [property: "og:image:width", content: "800"], []} in result
+ assert {:meta, [property: "og:image:height", content: "600"], []} in result
+
+ assert {:meta,
+ [
+ property: "og:image",
+ content:
+ "http://localhost:4001/proxy/preview/LzAnlke-l5oZbNzWsrHfprX1rGw/aHR0cHM6Ly9wbGVyb21hLmdvdi9hYm91dC9qdWNoZS53ZWJt/juche.webm"
+ ], []} in result
+ end
+
+ test "video attachments have no image thumbnail with Preview Proxy disabled" do
+ clear_config([:media_proxy, :enabled], true)
+ clear_config([:media_preview_proxy, :enabled], false)
+ user = insert(:user)
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "test video post",
+ "sensitive" => false,
+ "attachment" => [
+ %{
+ "url" => [
+ %{
+ "mediaType" => "video/webm",
+ "href" => "https://pleroma.gov/about/juche.webm",
+ "height" => 600,
+ "width" => 800
+ }
+ ]
+ }
+ ]
+ }
+ })
+
+ result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
+
+ refute {:meta, [property: "og:image:width", content: "800"], []} in result
+ refute {:meta, [property: "og:image:height", content: "600"], []} in result
+
+ refute {:meta,
+ [
+ property: "og:image",
+ content:
+ "http://localhost:4001/proxy/preview/LzAnlke-l5oZbNzWsrHfprX1rGw/aHR0cHM6Ly9wbGVyb21hLmdvdi9hYm91dC9qdWNoZS53ZWJt/juche.webm"
+ ], []} in result
+ end
end
diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs
index 196bca20a..1b8d27cda 100644
--- a/test/pleroma/web/metadata/providers/twitter_card_test.exs
+++ b/test/pleroma/web/metadata/providers/twitter_card_test.exs
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata.Providers.TwitterCard
alias Pleroma.Web.Metadata.Utils
alias Pleroma.Web.Router
@@ -17,7 +18,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
test "it renders twitter card for user info" do
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
- avatar_url = Utils.attachment_url(User.avatar_url(user))
+ avatar_url = MediaProxy.preview_url(User.avatar_url(user))
res = TwitterCard.build_tags(%{user: user})
assert res == [
@@ -111,7 +112,14 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
"content" => "pleroma in a nutshell",
"attachment" => [
%{
- "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}]
+ "url" => [
+ %{
+ "mediaType" => "image/png",
+ "href" => "https://pleroma.gov/tenshi.png",
+ "height" => 1024,
+ "width" => 1280
+ }
+ ]
},
%{
"url" => [
@@ -123,7 +131,12 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
},
%{
"url" => [
- %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
+ %{
+ "mediaType" => "video/webm",
+ "href" => "https://pleroma.gov/about/juche.webm",
+ "height" => 600,
+ "width" => 800
+ }
]
}
]
@@ -137,14 +150,16 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
{:meta, [property: "twitter:description", content: "pleroma in a nutshell"], []},
{:meta, [property: "twitter:card", content: "summary_large_image"], []},
{:meta, [property: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []},
+ {:meta, [property: "twitter:player:width", content: "1280"], []},
+ {:meta, [property: "twitter:player:height", content: "1024"], []},
{:meta, [property: "twitter:card", content: "player"], []},
{:meta,
[
property: "twitter:player",
content: Router.Helpers.o_status_url(Endpoint, :notice_player, activity.id)
], []},
- {:meta, [property: "twitter:player:width", content: "480"], []},
- {:meta, [property: "twitter:player:height", content: "480"], []},
+ {:meta, [property: "twitter:player:width", content: "800"], []},
+ {:meta, [property: "twitter:player:height", content: "600"], []},
{:meta,
[
property: "twitter:player:stream",