lib/seo/open_graph/video.ex

defmodule SEO.OpenGraph.Video do
  @moduledoc """
  Data describing a video.

  Commonly, an `og:video` is accompanied with an `og:image` that provides the preview image for the video.

  ### Resources

  - https://ogp.me/#structured
  - https://developers.facebook.com/docs/sharing/webmasters/#video
  - https://yandex.com/support/video/partners/open-graph.html
  """

  use Phoenix.Component
  alias SEO.Utils

  defstruct [
    :url,
    :secure_url,
    :type,
    :width,
    :height,
    :ya_bitrate,
    :ya_quality,
    :ya_allow_embed,
    :alt
  ]

  @type t :: %__MODULE__{
          url: URI.t() | String.t(),
          secure_url: URI.t() | String.t() | nil,
          type: mime(),
          width: pixels(),
          height: pixels(),
          ya_bitrate: yandex_bitrate(),
          ya_quality: yandex_quality(),
          ya_allow_embed: boolean() | nil,
          alt: String.t() | nil
        }

  @type mime :: String.t() | nil
  @type pixels :: pos_integer() | nil
  @type yandex_bitrate :: pos_integer() | nil
  @type yandex_quality :: :low | :medium | :hd | :full_hd | nil

  @doc """
  The `og:video` property has some optional structured properties:

  - `:url` - The url with metadata that describes the video.
  - `:secure_url` - An alternate url to use if the webpage requires HTTPS. If the URL starts with `https` then
  - `:type` - A MIME type for this video. Facebook supports both mp4 and Flash videos, `application/x-shockwave-flash`
    or `video/mp4`, but please for the love of all that is holy don't use Shockwave Flash.
  - `:width` - The width in pixels.
  - `:height` - The height in pixels.
  - `:alt` - A description of what is in the video (not a caption). If the page specifies an `og:video` it should
    specify `og:video:alt`.

  Supply a secure URL for both the `og:video:url` and `og:video:secure_url` tags to make your video eligible to play
  in-line in Facebook's Feed.

  For Yandex, there are additional properties:

  - `:ya_bitrate` - Maximum bitrate in kilobits per second
  - `:ya_allow_embed` - Allow embedding in Yandex search results
  - `:ya_quality` - The quality in resolution and bitrate
    - `:low` - Low quality with resolution less than 360x640 and bitrate lower than 717kbps
    - `:medium` - Average quality with resolution between 360x640 and 720x1280 and bitrate between than 717kbps-1Mbps
    - `:hd` - HD quality with resolution between 720x1280 and 1080x1920 and bitrate between than 1MBps-2Mbps
    - `:full_hd` - 1080p quality with resolution greater than 1080x1920 and bitrate higher than 2Mbps
  """

  @spec build(SEO.attrs(), SEO.config()) :: t() | nil
  def build(attrs, default \\ nil)

  def build(attrs, default) do
    __MODULE__
    |> Utils.merge_defaults(attrs, default)
    |> maybe_put_secure_url()
  end

  defp maybe_put_secure_url(video) do
    case video.url do
      %URI{scheme: "https"} = uri -> %{video | secure_url: uri}
      "https" <> _ = url -> %{video | secure_url: url}
      _ -> video
    end
  end

  attr(:content, :any, default: nil)

  def meta(assigns) do
    case assigns[:content] do
      nil ->
        ~H""

      %__MODULE__{} ->
        ~H"""
        <%= if @content.url || @content.secure_url do %>
        <%= if @content.url do %>
        <Utils.url property="og:video" content={@content.url} />
        <% end %><%= if @content.secure_url do %>
        <Utils.url property="og:video:secure_url" content={@content.secure_url} />
        <% end %><%= if @content.type do %>
        <meta property="og:video:type" content={@content.type} />
        <% end %><%= if @content.width do %>
        <meta property="og:video:width" content={@content.width} />
        <% end %><%= if @content.height do %>
        <meta property="og:video:height" content={@content.height} />
        <% end %><%= if @content.alt do %>
        <meta property="og:video:alt" content={@content.alt} />
        <% end %><%= if @content.ya_bitrate do %>
        <meta property="ya:ovs:bitrate" content={@content.ya_bitrate} />
        <% end %><%= if @content.ya_quality do %>
        <meta property="ya:ovs:quality" content={format_ya_quality(@content.ya_quality)} />
        <% end %><%= if @content.ya_allow_embed do %>
        <meta property="ya:ovs:allow_embed" content={"#{@content.ya_allow_embed}"} />
        <% end %>
        <% end %>
        """

      _url ->
        ~H"""
        <Utils.url property="og:video" content={@content} />
        """
    end
  end

  defp format_ya_quality(nil), do: nil
  defp format_ya_quality(:low), do: "low"
  defp format_ya_quality(:medium), do: "medium"
  defp format_ya_quality(:hd), do: "HD"
  defp format_ya_quality(:full_hd), do: "full HD"
end