diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index e78f347..7a8d8ad 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -51,6 +51,11 @@ defmodule Jellyfish.Room do """ @type id :: String.t() + @typedoc """ + Id of the track, unique within Jellyfish instance. + """ + @type track_id :: String.t() + @typedoc """ Peer token, created by Jellyfish. Required by client application to open connection to Jellyfish. """ @@ -236,6 +241,20 @@ defmodule Jellyfish.Room do end end + @doc """ + Adds tracks to hls component + """ + @spec hls_subscribe(Client.t(), id(), [track_id()]) :: :ok | {:error, atom() | String.t()} + def hls_subscribe(client, room_id, tracks) do + with :ok <- validate_tracks(tracks), + {:ok, %Env{status: 201}} <- + Tesla.post(client.http_client, "/hls/#{room_id}/subscribe", %{tracks: tracks}) do + :ok + else + error -> Utils.handle_response_error(error) + end + end + @doc false @spec from_json(map()) :: t() def from_json(response) do @@ -285,6 +304,9 @@ defmodule Jellyfish.Room do defp validate_subscribe_mode(mode) when mode in @subscribe_modes, do: :ok defp validate_subscribe_mode(_mode), do: :error + defp validate_tracks(tracks) when is_list(tracks), do: :ok + defp validate_tracks(_tracks), do: {:error, :tracks_validation} + defp map_snake_case_to_camel_case(%{} = map), do: Map.new(map, fn {k, v} -> {snake_case_to_camel_case(k), map_snake_case_to_camel_case(v)} end) diff --git a/test/jellyfish/room_test.exs b/test/jellyfish/room_test.exs index bf06a61..6d0a57a 100644 --- a/test/jellyfish/room_test.exs +++ b/test/jellyfish/room_test.exs @@ -41,6 +41,9 @@ defmodule Jellyfish.RoomTest do @invalid_max_peers "abc" @invalid_video_codec :opus + @tracks ["track-id"] + @invalid_tracks %{id: "track-id"} + @invalid_peer_id "invalid_peer_id" defmodule InvalidPeerOpts do defstruct [:qwe, :rty] @@ -261,6 +264,42 @@ defmodule Jellyfish.RoomTest do end end + describe "Room.hls_subscribe/3" do + setup [:create_room] + + test "when request is valid", %{client: client, room_id: room_id} do + assert {:ok, %Component{metadata: %{subscribe_mode: "manual"}}} = + Room.add_component(client, room_id, %Component.HLS{subscribe_mode: :manual}) + + assert :ok = Room.hls_subscribe(client, room_id, @tracks) + end + + test "when room doesn't exist", %{client: client} do + assert {:error, "Request failed: Room #{@invalid_room_id} does not exist"} = + Room.hls_subscribe(client, @invalid_room_id, @tracks) + end + + test "when hls component doesn't exist", %{client: client, room_id: room_id} do + assert {:error, "Request failed: HLS component does not exist"} = + Room.hls_subscribe(client, room_id, @tracks) + end + + test "when hls component has subscribe mode :auto", %{client: client, room_id: room_id} do + assert {:ok, %Component{metadata: %{subscribe_mode: "auto"}}} = + Room.add_component(client, room_id, %Jellyfish.Component.HLS{subscribe_mode: :auto}) + + assert {:error, "Request failed: HLS component option `subscribe_mode` is set to :auto"} = + Room.hls_subscribe(client, room_id, @tracks) + end + + test "when request is invalid", %{client: client, room_id: room_id} do + assert {:ok, %Component{metadata: %{subscribe_mode: "manual"}}} = + Room.add_component(client, room_id, %Component.HLS{subscribe_mode: :manual}) + + assert {:error, :tracks_validation} = Room.hls_subscribe(client, room_id, @invalid_tracks) + end + end + defp create_room(state) do assert {:ok, %Jellyfish.Room{id: id}, _jellyfish_address} = Room.create(state.client, max_peers: @max_peers, video_codec: @video_codec)