defmodule TribeOne.TribesPlugin.SenderWeb.StreamingAPIPlug do @moduledoc false import Plug.Conn alias TribeOne.TribesPlugin.Sender.{Playback, Stats} alias Tribes.RequestInfo def init(opts), do: opts def call( %{ method: "GET", path_info: ["plugins-api", "tribe-one-sender", "streams", stream_id, "playback"] } = conn, _opts ) do case Playback.playback_info(stream_id) do {:ok, payload} -> json(conn, 200, payload) {:error, :not_live} -> json(conn, 404, %{"error" => "not_live"}) {:error, _reason} -> json(conn, 404, %{"error" => "not_found"}) end end def call( %{method: "POST", path_info: ["plugins-api", "tribe-one-sender", "player-events"]} = conn, _opts ) do {event, body, conn} = read_event(conn) request_info = RequestInfo.fetch(conn) record_result = record_player_event(event, request_info) :telemetry.execute( [:sender, :player, :event], %{count: 1}, %{ body: body, event: event, remote_ip: conn.remote_ip, request_ip: request_info.ip, trusted_proxy?: request_info.trusted_proxy? } ) json(conn, 202, player_event_response(record_result)) end def call(conn, _opts) do json(conn, 404, %{"error" => "not_found"}) end defp json(conn, status, payload) do conn |> put_resp_content_type("application/json") |> send_resp(status, JSON.encode!(payload)) end defp read_event(conn) do cond do event_params?(conn.body_params) -> {conn.body_params, JSON.encode!(conn.body_params), conn} event_params?(conn.params) -> {conn.params, JSON.encode!(conn.params), conn} true -> read_raw_event(conn) end end defp read_raw_event(conn) do {:ok, body, conn} = read_body(conn) {decode_body(body), body, conn} end defp decode_body(body) do case JSON.decode(body) do {:ok, decoded} when is_map(decoded) -> decoded _other -> %{} end end defp record_player_event( %{ "event" => event, "stream_id" => stream_id, "generation_id" => generation_id, "viewer_session_id" => viewer_session_id }, %RequestInfo{trusted_proxy?: false, ip: remote_ip} ) when event in ["heartbeat", "playback.started"] do Stats.record_viewer_heartbeat(stream_id, generation_id, viewer_session_id, remote_ip) end defp record_player_event(_event, _request_info), do: :ignored defp event_params?(%Plug.Conn.Unfetched{}), do: false defp event_params?(params) when is_map(params), do: map_size(params) > 0 defp event_params?(_params), do: false defp player_event_response(viewer_count) when is_integer(viewer_count) do %{"ok" => true, "viewer_count" => viewer_count} end defp player_event_response(_record_result), do: %{"ok" => true} end