X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Frich_media%2Fparser.ex;h=e4595800c2f04afebbb40b381d5a28aa2eaf9b8a;hb=f497cf2f7c21196c4d633ddf0d14f0a4ede00cea;hp=18d9e2df5cdb56f28c8cd31ef788c9a3ae5518fe;hpb=7f5efddd6ed60fead5b456186b67a96dde95e6b9;p=akkoma diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 18d9e2df5..40980def8 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -1,21 +1,109 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.RichMedia.Parser do - @parsers [Pleroma.Web.RichMedia.Parsers.OGP] + @options [ + pool: :media, + max_body: 2_000_000 + ] + + defp parsers do + Pleroma.Config.get([:rich_media, :parsers]) + end + + def parse(nil), do: {:error, "No URL provided"} - if Mix.env() == :test do + if Pleroma.Config.get(:env) == :test do def parse(url), do: parse_url(url) else - def parse(url), - do: Cachex.fetch!(:rich_media_cache, url, fn _ -> parse_url(url) end) + def parse(url) do + try do + Cachex.fetch!(:rich_media_cache, url, fn _ -> + {:commit, parse_url(url)} + end) + |> set_ttl_based_on_image(url) + rescue + e -> + {:error, "Cachex error: #{inspect(e)}"} + end + end + end + + @doc """ + Set the rich media cache based on the expiration time of image. + + Adopt behaviour `Pleroma.Web.RichMedia.Parser.TTL` + + ## Example + + defmodule MyModule do + @behaviour Pleroma.Web.RichMedia.Parser.TTL + def ttl(data, url) do + image_url = Map.get(data, :image) + # do some parsing in the url and get the ttl of the image + # and return ttl is unix time + parse_ttl_from_url(image_url) + end + end + + Define the module in the config + + config :pleroma, :rich_media, + ttl_setters: [MyModule] + """ + def set_ttl_based_on_image({:ok, data}, url) do + with {:ok, nil} <- Cachex.ttl(:rich_media_cache, url), + ttl when is_number(ttl) <- get_ttl_from_image(data, url) do + Cachex.expire_at(:rich_media_cache, url, ttl * 1000) + {:ok, data} + else + _ -> + {:ok, data} + end + end + + defp get_ttl_from_image(data, url) do + Pleroma.Config.get([:rich_media, :ttl_setters]) + |> Enum.reduce({:ok, nil}, fn + module, {:ok, _ttl} -> + module.ttl(data, url) + + _, error -> + error + end) end defp parse_url(url) do - {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url) + opts = + if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do + Keyword.merge(@options, + recv_timeout: 2_000, + with_body: true + ) + else + @options + end + + try do + {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: opts) - html |> maybe_parse() |> get_parsed_data() + html + |> parse_html() + |> maybe_parse() + |> Map.put(:url, url) + |> clean_parsed_data() + |> check_parsed_data() + rescue + e -> + {:error, "Parsing error: #{inspect(e)} #{inspect(__STACKTRACE__)}"} + end end + defp parse_html(html), do: Floki.parse_document!(html) + defp maybe_parse(html) do - Enum.reduce_while(@parsers, %{}, fn parser, acc -> + Enum.reduce_while(parsers(), %{}, fn parser, acc -> case parser.parse(html, acc) do {:ok, data} -> {:halt, data} {:error, _msg} -> {:cont, acc} @@ -23,11 +111,24 @@ defmodule Pleroma.Web.RichMedia.Parser do end) end - defp get_parsed_data(data) when data == %{} do - {:error, "No metadata found"} + defp check_parsed_data(%{title: title} = data) + when is_binary(title) and byte_size(title) > 0 do + {:ok, data} end - defp get_parsed_data(data) do - {:ok, data} + defp check_parsed_data(data) do + {:error, "Found metadata was invalid or incomplete: #{inspect(data)}"} + end + + defp clean_parsed_data(data) do + data + |> Enum.reject(fn {key, val} -> + with {:ok, _} <- Jason.encode(%{key => val}) do + false + else + _ -> true + end + end) + |> Map.new() end end