feed_parser/lib/parser/jsonfeed.ex

100 lines
2.2 KiB
Elixir

defmodule FeedParser.Parser.JSONFeed do
@moduledoc """
A `FeedParser.Parser` that handles [JSON Feeds](https://jsonfeed.org/version/1).
"""
@behaviour FeedParser.Parser
@mime_types [
"application/json",
"application/feed+json"
]
@versions [
"https://jsonfeed.org/version/1",
"https://jsonfeed.org/version/1.1"
]
@impl FeedParser.Parser
def accepts(data, content_type) do
with true <- content_type in @mime_types,
{:ok, json} <- Poison.decode(data),
%{"version" => v} when v in @versions <- json do
{true, json}
else
_ ->
false
end
end
@impl FeedParser.Parser
def parse_feed(json) do
title = json["title"]
home_page_url = Map.get(json, "home_page_url")
icon = Map.get(json, "icon") || Map.get(json, "favicon")
feed_author = authors_string(json)
items =
Map.get(json, "items", [])
|> Enum.map(fn item ->
id = item["id"]
url =
Map.get(item, "url") ||
if String.starts_with?(id, ["http://", "https://"]), do: id, else: nil
title = Map.get(item, "title")
content =
Map.get(item, "content_html") || Map.get(item, "content_text") ||
Map.get(item, "summary")
date =
(Map.get(item, "date_published") || Map.get(item, "date_updated"))
|> Timex.parse("{RFC3339}")
|> case do
{:ok, date} -> date
_ -> nil
end
author = authors_string(item) || feed_author
%FeedParser.Item{
guid: id,
url: url,
links: [{url, nil}],
title: title,
content: content,
date: date,
creator: author
}
end)
{:ok,
%FeedParser.Feed{
site_url: home_page_url,
title: title,
image_url: icon,
last_updated: nil,
items: items
}}
end
defp authors_string(%{"author" => author}) do
author_name(author)
end
defp authors_string(%{"authors" => authors}) do
authors
|> Enum.map(&author_name/1)
|> Enum.reject(&is_nil/1)
|> Enum.join(", ")
end
defp authors_string(_), do: nil
defp author_name(%{"name" => name}), do: name
defp author_name(_), do: nil
end