Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85710329
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pleroma/web/rich_media/parser/card.ex b/lib/pleroma/web/rich_media/parser/card.ex
index 2e06aa562..1eb3ddbdd 100644
--- a/lib/pleroma/web/rich_media/parser/card.ex
+++ b/lib/pleroma/web/rich_media/parser/card.ex
@@ -1,112 +1,112 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.Card do
alias Pleroma.Web.RichMedia.Parser.Card
alias Pleroma.Web.RichMedia.Parser.Embed
@types ["link", "photo", "video", "rich"]
# https://docs.joinmastodon.org/entities/card/
defstruct url: nil,
title: nil,
description: "",
type: "link",
author_name: "",
author_url: "",
provider_name: "",
provider_url: "",
html: "",
width: 0,
height: 0,
image: nil,
embed_url: "",
blurhash: nil
def parse(%{url: url, oembed: %{"type" => type, "title" => title} = oembed} = embed)
when type in @types do
%Card{
url: url,
title: title,
description: get_description(embed),
type: oembed["type"],
author_name: oembed["author_name"],
author_url: oembed["author_url"],
provider_name: oembed["provider_name"],
provider_url: oembed["provider_url"],
html: oembed["html"],
width: oembed["width"],
height: oembed["height"],
image: oembed["thumbnail_url"] |> proxy(),
embed_url: oembed["url"] |> proxy()
}
|> validate()
end
def parse(%{url: url} = embed) do
%Card{
url: url,
title: get_title(embed),
description: get_description(embed),
type: "link",
image: get_image(embed) |> proxy()
}
|> validate()
end
def parse(card), do: {:error, {:invalid_metadata, card}}
defp get_title(embed) do
case embed do
%{meta: %{"twitter:title" => title}} when is_binary(title) and title != "" -> title
%{meta: %{"og:title" => title}} when is_binary(title) and title != "" -> title
%{title: title} when is_binary(title) and title != "" -> title
_ -> nil
end
end
defp get_description(%{meta: meta}) do
case meta do
%{"twitter:description" => desc} when is_binary(desc) and desc != "" -> desc
%{"og:description" => desc} when is_binary(desc) and desc != "" -> desc
%{"description" => desc} when is_binary(desc) and desc != "" -> desc
_ -> ""
end
end
defp get_image(%{meta: meta}) do
case meta do
%{"twitter:image" => image} when is_binary(image) and image != "" -> image
%{"og:image" => image} when is_binary(image) and image != "" -> image
_ -> ""
end
end
def to_map(%Card{} = card) do
card
|> Map.from_struct()
|> stringify_keys()
end
def to_map(%{} = card), do: stringify_keys(card)
defp stringify_keys(%{} = map), do: Map.new(map, fn {k, v} -> {Atom.to_string(k), v} end)
defp proxy(url) when is_binary(url), do: Pleroma.Web.MediaProxy.url(url)
defp proxy(_), do: nil
def validate(%Card{type: type, title: title} = card)
when type in @types and is_binary(title) and title != "" do
{:ok, card}
end
def validate(%Embed{} = embed) do
case Card.parse(embed) do
- %Card{} = card -> validate(card)
+ {:ok, %Card{} = card} -> validate(card)
card -> {:error, {:invalid_metadata, card}}
end
end
def validate(card), do: {:error, {:invalid_metadata, card}}
end
diff --git a/lib/pleroma/web/rich_media/parser/meta_tags.ex b/lib/pleroma/web/rich_media/parser/meta_tags.ex
index e5c6b448d..ea544b110 100644
--- a/lib/pleroma/web/rich_media/parser/meta_tags.ex
+++ b/lib/pleroma/web/rich_media/parser/meta_tags.ex
@@ -1,30 +1,39 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.MetaTags do
@doc """
Parses a `Floki.html_tree/0` and returns a map of raw `<meta>` tag values.
"""
@spec parse(html_tree :: Floki.html_tree()) :: map()
def parse(html_tree) do
html_tree
|> Floki.find("meta")
|> Enum.reduce(%{}, fn html_node, acc ->
case parse_node(html_node) do
{:ok, {name, content}} -> Map.put(acc, name, content)
_ -> acc
end
end)
+ |> clean_data()
end
defp parse_node({_tag, attrs, _children}) when is_list(attrs) do
case Map.new(attrs) do
%{"name" => name, "content" => content} -> {:ok, {name, content}}
%{"property" => name, "content" => content} -> {:ok, {name, content}}
_ -> {:error, :invalid_meta_tag}
end
end
defp parse_node(_), do: {:error, :invalid_meta_tag}
+
+ defp clean_data(data) do
+ data
+ |> Enum.reject(fn {key, val} ->
+ not match?({:ok, _}, Jason.encode(%{key => val}))
+ end)
+ |> Map.new()
+ end
end
diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
index a892d16ea..4785ac6df 100644
--- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex
+++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
@@ -1,23 +1,24 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
alias Pleroma.Web.RichMedia.Parser.MetaTags
@spec parse(Floki.html_tree(), map()) :: map()
def parse(html, data) do
data
|> Map.put(:title, get_page_title(html))
|> Map.put(:meta, MetaTags.parse(html))
end
def get_page_title(html) do
with [node | _] <- Floki.find(html, "html head title"),
- title when is_binary(title) and title != "" <- Floki.text(node) do
+ title when is_binary(title) and title != "" <- Floki.text(node),
+ true <- String.valid?(title) do
title
else
_ -> nil
end
end
end
diff --git a/test/pleroma/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs
index 2f363b012..f7d010219 100644
--- a/test/pleroma/web/rich_media/parser_test.exs
+++ b/test/pleroma/web/rich_media/parser_test.exs
@@ -1,176 +1,184 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.ParserTest do
use ExUnit.Case, async: true
alias Pleroma.Web.RichMedia.Parser
+ alias Pleroma.Web.RichMedia.Parser.Embed
setup do
Tesla.Mock.mock(fn
%{
method: :get,
url: "http://example.com/ogp"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
%{
method: :get,
url: "http://example.com/non-ogp"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}
%{
method: :get,
url: "http://example.com/ogp-missing-title"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
}
%{
method: :get,
url: "http://example.com/twitter-card"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
%{
method: :get,
url: "http://example.com/oembed"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}
%{
method: :get,
url: "http://example.com/oembed.json"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}
%{method: :get, url: "http://example.com/empty"} ->
%Tesla.Env{status: 200, body: "hello"}
%{method: :get, url: "http://example.com/malformed"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}
%{method: :get, url: "http://example.com/error"} ->
{:error, :overload}
%{
method: :head,
url: "http://example.com/huge-page"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
}
%{
method: :head,
url: "http://example.com/pdf-file"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
}
%{method: :head} ->
%Tesla.Env{status: 404, body: "", headers: []}
end)
:ok
end
test "returns error when no metadata present" do
assert {:error, _} = Parser.parse("http://example.com/empty")
end
- test "doesn't just add a title" do
- assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp")
- end
-
test "parses ogp" do
- assert Parser.parse("http://example.com/ogp") ==
- {:ok,
- %{
- "image" => "http://ia.media-imdb.com/images/rock.jpg",
- "title" => "The Rock",
- "description" =>
- "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
- "type" => "video.movie",
- "url" => "http://example.com/ogp"
- }}
+ url = "http://example.com/ogp"
+
+ expected = %Embed{
+ meta: %{
+ "og:image" => "http://ia.media-imdb.com/images/rock.jpg",
+ "og:title" => "The Rock",
+ "og:description" =>
+ "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
+ "og:type" => "video.movie",
+ "og:url" => "http://www.imdb.com/title/tt0117500/"
+ },
+ oembed: nil,
+ title: "The Rock (1996)",
+ url: "http://example.com/ogp"
+ }
+
+ assert Parser.parse(url) == {:ok, expected}
end
- test "falls back to <title> when ogp:title is missing" do
- assert Parser.parse("http://example.com/ogp-missing-title") ==
- {:ok,
- %{
- "image" => "http://ia.media-imdb.com/images/rock.jpg",
- "title" => "The Rock (1996)",
- "description" =>
- "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
- "type" => "video.movie",
- "url" => "http://example.com/ogp-missing-title"
- }}
+ test "gets <title> tag" do
+ url = "http://example.com/ogp-missing-title"
+ expected = "The Rock (1996)"
+ assert {:ok, %Embed{title: ^expected}} = Parser.parse(url)
end
test "parses twitter card" do
- assert Parser.parse("http://example.com/twitter-card") ==
- {:ok,
- %{
- "card" => "summary",
- "site" => "@flickr",
- "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg",
- "title" => "Small Island Developing States Photo Submission",
- "description" => "View the album on Flickr.",
- "url" => "http://example.com/twitter-card"
- }}
+ url = "http://example.com/twitter-card"
+
+ expected = %Embed{
+ meta: %{
+ "twitter:card" => "summary",
+ "twitter:description" => "View the album on Flickr.",
+ "twitter:image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg",
+ "twitter:site" => "@flickr",
+ "twitter:title" => "Small Island Developing States Photo Submission"
+ },
+ oembed: nil,
+ title: nil,
+ url: "http://example.com/twitter-card"
+ }
+
+ assert Parser.parse(url) == {:ok, expected}
end
test "parses OEmbed" do
- assert Parser.parse("http://example.com/oembed") ==
- {:ok,
- %{
- "author_name" => "bees",
- "author_url" => "https://www.flickr.com/photos/bees/",
- "cache_age" => 3600,
- "flickr_type" => "photo",
- "height" => "768",
- "html" =>
- "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
- "license" => "All Rights Reserved",
- "license_id" => 0,
- "provider_name" => "Flickr",
- "provider_url" => "https://www.flickr.com/",
- "thumbnail_height" => 150,
- "thumbnail_url" =>
- "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
- "thumbnail_width" => 150,
- "title" => "Bacon Lollys",
- "type" => "photo",
- "url" => "http://example.com/oembed",
- "version" => "1.0",
- "web_page" => "https://www.flickr.com/photos/bees/2362225867/",
- "web_page_short_url" => "https://flic.kr/p/4AK2sc",
- "width" => "1024"
- }}
+ url = "http://example.com/oembed"
+
+ expected = %Embed{
+ meta: %{},
+ oembed: %{
+ "author_name" => "bees",
+ "author_url" => "https://www.flickr.com/photos/bees/",
+ "cache_age" => 3600,
+ "flickr_type" => "photo",
+ "height" => "768",
+ "html" =>
+ "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
+ "license" => "All Rights Reserved",
+ "license_id" => 0,
+ "provider_name" => "Flickr",
+ "provider_url" => "https://www.flickr.com/",
+ "thumbnail_height" => 150,
+ "thumbnail_url" => "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
+ "thumbnail_width" => 150,
+ "title" => "Bacon Lollys",
+ "type" => "photo",
+ "url" => "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
+ "version" => "1.0",
+ "web_page" => "https://www.flickr.com/photos/bees/2362225867/",
+ "web_page_short_url" => "https://flic.kr/p/4AK2sc",
+ "width" => "1024"
+ },
+ url: "http://example.com/oembed"
+ }
+
+ assert Parser.parse(url) == {:ok, expected}
end
test "rejects invalid OGP data" do
assert {:error, _} = Parser.parse("http://example.com/malformed")
end
test "returns error if getting page was not successful" do
assert {:error, :overload} = Parser.parse("http://example.com/error")
end
test "does a HEAD request to check if the body is too large" do
assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page")
end
test "does a HEAD request to check if the body is html" do
assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file")
end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Sep 18, 11:26 PM (6 h, 58 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1768450
Default Alt Text
(15 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment