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