lib/aliyun_oss/live_channel.ex

defmodule Aliyun.Oss.LiveChannel do
  @moduledoc """
  LiveChannel-related operations.
  """

  import Aliyun.Oss.Bucket, only: [get_bucket: 4]
  import Aliyun.Oss.Object, only: [put_object: 6, get_object: 5, delete_object: 4]
  alias Aliyun.Oss.Config
  alias Aliyun.Oss.Service
  alias Aliyun.Oss.Client.{Request, Response, Error}

  @type error() ::
          %Error{body: String.t(), status_code: integer(), parsed_details: map()} | atom()

  @doc """
  Creates a signed RTMP ingest URL.

  ## Examples

      iex> expires = Timex.now() |> Timex.shift(days: 1) |> Timex.to_unix()
      iex> Aliyun.Oss.Object.signed_url("some-bucket", "some-object", expires, "GET", %{"Content-Type" -> ""})
      "http://some-bucket.oss-cn-hangzhou.aliyuncs.com/oss-api.pdf?OSSAccessKeyId=nz2pc5*******9l&Expires=1141889120&Signature=vjbyPxybdZ*****************v4%3D"

  """
  @spec signed_publish_url(Config.t(), String.t(), String.t(), integer(), map()) :: String.t()
  def signed_publish_url(
        config,
        bucket,
        channel,
        expires,
        query_params \\ %{"playlistName" => "playlist.m3u8"}
      ) do
    request =
      Request.build(%{
        host: "#{bucket}.#{config.endpoint}",
        path: "/live/#{channel}",
        resource: "/#{bucket}/#{channel}",
        scheme: "rtmp",
        query_params: query_params,
        expires: expires
      })

    Request.to_signed_url(config, request)
  end

  @doc """
  PutLiveChannel - creates a LiveChannel.

  ## Examples

      iex> config_json = %{
        "LiveChannelConfiguration" => %{
          "Description" => nil,
          "Status" => "enabled",
          "Target" => %{"FragCount" => "3", "FragDuration" => "2", "Type" => "HLS"}
        }
      }
      iex> Aliyun.Oss.LiveChannel.put("some-bucket", "channe-name", config_json)
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{
            "CreateLiveChannelResult" => %{
              "PlayUrls" => %{
                "Url" => "http://some-bucket.oss-cn-shenzhen.aliyuncs.com/channel-name/playlist.m3u8"
              },
              "PublishUrls" => %{
                "Url" => "rtmp://some-bucket.oss-cn-shenzhen.aliyuncs.com/live/channel-name"
              }
            }
          },
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }
      iex> config_xml = ~S[
        <?xml version="1.0" encoding="UTF-8"?>
        <LiveChannelConfiguration>
          <Description></Description>
          <Status>enabled</Status>
          <Target>
            <FragCount>3</FragCount>
            <FragDuration>2</FragDuration>
            <Type>HLS</Type>
          </Target>
        </LiveChannelConfiguration>
      ]
      iex> Aliyun.Oss.LiveChannel.put("some-bucket", "channe-name", config_xml)
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{
            "CreateLiveChannelResult" => %{
              "PlayUrls" => %{
                "Url" => "http://some-bucket.oss-cn-shenzhen.aliyuncs.com/channel-name/playlist.m3u8"
              },
              "PublishUrls" => %{
                "Url" => "rtmp://some-bucket.oss-cn-shenzhen.aliyuncs.com/live/channel-name"
              }
            }
          },
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec put(Config.t(), String.t(), String.t(), map() | String.t()) ::
          {:error, error()} | {:ok, Response.t()}
  def put(config, bucket, channel_name, %{} = config) do
    put(config, bucket, channel_name, MapToXml.from_map(config))
  end

  def put(config, bucket, channel_name, config) do
    put_object(config, bucket, channel_name, config, %{}, %{"live" => nil})
  end

  @doc """
  ListLiveChannel - lists specified LiveChannels.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.list("some-bucket")
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{
            "ListLiveChannelResult" => %{
              "IsTruncated" => false,
              "LiveChannel" => [
                %{
                  "Description" => nil,
                  "LastModified" => "2021-01-20T03:18:06.000Z",
                  "Name" => "channel1",
                  "PlayUrls" => %{
                    "Url" => "http://some-bucket.oss-cn-shenzhen.aliyuncs.com/channel1/playlist.m3u8"
                  },
                  "PublishUrls" => %{
                    "Url" => "rtmp://some-bucket.oss-cn-shenzhen.aliyuncs.com/live/channel1"
                  },
                  "Status" => "enabled"
                },
                # ..
              ],
              "Marker" => nil,
              "MaxKeys" => 2,
              "NextMarker" => nil,
              "Prefix" => nil
            }
          },
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec list(Config.t(), String.t(), map()) :: {:error, error()} | {:ok, Response.t()}
  def list(config, bucket, query_params \\ %{}) do
    get_bucket(config, bucket, query_params, %{"live" => nil})
  end

  @doc """
  DeleteLiveChannel - deletes a specified LiveChannel.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.delete("some-bucket", "channel-name")
      {:ok, %Aliyun.Oss.Client.Response{
          data: "",
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec delete(Config.t(), String.t(), String.t()) :: {:error, error()} | {:ok, Response.t()}
  def delete(config, bucket, channel_name) do
    delete_object(config, bucket, channel_name, %{"live" => nil})
  end

  @doc """
  PutLiveChannelStatus - sets a LiveChannel to one of the states - enabled or disabled.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.put_status("some-bucket", "channe-name", "disabled)
      {:ok, %Aliyun.Oss.Client.Response{
          data: "",
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec put_status(Config.t(), String.t(), String.t(), String.t()) ::
          {:error, error()} | {:ok, Response.t()}
  def put_status(config, bucket, channel_name, status) do
    put_object(config, bucket, channel_name, "", %{}, %{"live" => nil, "status" => status})
  end

  @doc """
  GetLiveChannelInfo - gets the configuration information about a specified LiveChannel.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.get_info("some-bucket", "channe-name")
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{
            "LiveChannelConfiguration" => %{
              "Description" => nil,
              "Status" => "enabled",
              "Target" => %{
                "FragCount" => "3",
                "FragDuration" => "2",
                "PlaylistName" => "playlist.m3u8",
                "Type" => "HLS"
              }
            }
          },
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec get_info(Config.t(), String.t(), String.t()) :: {:error, error()} | {:ok, Response.t()}
  def get_info(config, bucket, channel_name) do
    get_object(config, bucket, channel_name, %{}, %{"live" => nil})
  end

  @doc """
  GetLiveChannelStat - gets the stream ingestion status of a specified LiveChannel.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.get_stat("some-bucket", "channe-name")
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{"LiveChannelStat" => %{"Status" => "Idle"}},
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec get_stat(Config.t(), String.t(), String.t()) :: {:error, error()} | {:ok, Response.t()}
  def get_stat(config, bucket, channel_name) do
    get_object(config, bucket, channel_name, %{}, %{"live" => nil, "comp" => "stat"})
  end

  @doc """
  GetLiveChannelHistory - gets the stream pushing record of a LiveChannel.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.get_history("some-bucket", "channe-name")
      {:ok, %Aliyun.Oss.Client.Response{
          data: %{"LiveChannelHistory" => %{
            "LiveRecord" => [
              # ...
            ]
          }},
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec get_history(Config.t(), String.t(), String.t()) :: {:error, error()} | {:ok, Response.t()}
  def get_history(config, bucket, channel_name) do
    get_object(config, bucket, channel_name, %{}, %{"live" => nil, "comp" => "history"})
  end

  @doc """
  PostVodPlaylist - generates a VOD playlist for the specified LiveChannel.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.post_playlist("some-bucket", "channe-name", "list.m3u8", 1472020031, 1472020226)
      {:ok, %Aliyun.Oss.Client.Response{
          data: "",
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec post_playlist(Config.t(), String.t(), String.t(), String.t(), integer(), integer()) ::
          {:error, error()} | {:ok, Response.t()}
  def post_playlist(config, bucket, channel_name, list_name, start_time, end_time) do
    Service.post(config, bucket, "#{channel_name}/#{list_name}", "",
      sub_resources: %{"vod" => nil, "startTime" => start_time, "endTime" => end_time}
    )
  end

  @doc """
  GetVodPlaylist - gets the playlist generated by the streams pushed to a specified LiveChannel in a specified time period.

  ## Examples

      iex> Aliyun.Oss.LiveChannel.get_playlist("some-bucket", "channe-name", 1472020031, 1472020226)
      {:ok, %Aliyun.Oss.Client.Response{
          data: "#EXTM3u...",
          headers: [
            {"Server", "AliyunOSS"},
            {"Date", "Wed, 05 Dec 2018 02:34:57 GMT"},
            ...
          ]
        }
      }

  """
  @spec get_playlist(Config.t(), String.t(), String.t(), integer(), integer()) ::
          {:error, error()} | {:ok, Response.t()}
  def get_playlist(config, bucket, channel_name, start_time, end_time) do
    get_object(config, bucket, channel_name, %{}, %{
      "vod" => nil,
      "startTime" => start_time,
      "endTime" => end_time
    })
  end
end