Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ room_id
# => "8878cd13-99a6-40d6-8d7e-8da23d803dab"

# Add peer
{:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Jellyfish.Room.add_peer(client, room_id, "webrtc")
{:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Jellyfish.Room.add_peer(client, room_id, Jellyfish.Peer.WebRTC)

receive do
{:jellyfish, {:peer_connected, ^room_id, ^peer_id}} -> # handle the notification
Expand Down
39 changes: 33 additions & 6 deletions lib/jellyfish/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Jellyfish.Component do
For more information refer to [Jellyfish documentation](https://www.membrane.stream)
"""

alias Jellyfish.Component.{HLS, RTSP}
alias Jellyfish.Exception.StructureError

@enforce_keys [
Expand All @@ -24,13 +25,19 @@ defmodule Jellyfish.Component do

For more information refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/).
"""
@type type :: String.t()
@type type :: :hls | :rtsp

@typedoc """
Type describing component options.
For the list of available options, please refer to the [documentation](https://jellyfish-dev.github.io/jellyfish-docs/).
Component-specific options.

For the list of available options, refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/).
"""
@type options :: HLS.t() | RTSP.t()

@typedoc """
Component options module.
"""
@type options :: Keyword.t()
@type options_module :: HLS | RTSP

@typedoc """
Stores information about the component.
Expand All @@ -40,21 +47,41 @@ defmodule Jellyfish.Component do
type: type()
}

@valid_type_strings ["hls", "rtsp"]

@doc false
@spec from_json(map()) :: t()
def from_json(response) do
case response do
%{
"id" => id,
"type" => type
"type" => type_str
} ->
%__MODULE__{
id: id,
type: type
type: type_from_string(type_str)
}

_other ->
raise StructureError
end
end

@doc false
@spec type_from_options(struct()) :: type()
def type_from_options(component) do
case component do
%HLS{} -> :hls
%RTSP{} -> :rtsp
_other -> raise "Invalid component options struct"
end
end

@doc false
@spec type_from_string(String.t()) :: type()
def type_from_string(string) do
if string in @valid_type_strings,
do: String.to_atom(string),
else: raise("Invalid component type string")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By convention, we call raise without parentheses (as you did in some other places).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm blaming mix format for this :PP

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it keeps adding the parentheses -- I'm afraid the mighty linter has spoken...

I guess the convention doesn't apply when using keyword list if syntax ¯_(ツ)_/¯

end
end
12 changes: 12 additions & 0 deletions lib/jellyfish/component/hls.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Jellyfish.Component.HLS do
@moduledoc """
Options for the HLS component.

For the description of these options refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/getting_started/components/hls)
"""

@enforce_keys []
defstruct @enforce_keys ++ []

@type t :: %__MODULE__{}
end
24 changes: 24 additions & 0 deletions lib/jellyfish/component/rtsp.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Jellyfish.Component.RTSP do
@moduledoc """
Options for the RTSP component.

For the description of these options refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/getting_started/components/rtsp)
"""

@enforce_keys [:source_uri]
defstruct @enforce_keys ++
[
rtp_port: 20_000,
reconnect_delay: 15_000,
keep_alive_interval: 15_000,
pierce_nat: true
]

@type t :: %__MODULE__{
source_uri: URI.t(),
rtp_port: 1..65_535,
reconnect_delay: non_neg_integer(),
keep_alive_interval: non_neg_integer(),
pierce_nat: boolean()
}
end
38 changes: 35 additions & 3 deletions lib/jellyfish/peer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Jellyfish.Peer do
"""

alias Jellyfish.Exception.StructureError
alias Jellyfish.Peer.WebRTC

@enforce_keys [
:id,
Expand All @@ -25,7 +26,19 @@ defmodule Jellyfish.Peer do

For more information refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/).
"""
@type type :: String.t()
@type type :: :webrtc

@typedoc """
Peer-specific options.

For the list of available options, refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/).
"""
@type options :: WebRTC.t()

@typedoc """
Peer options module.
"""
@type options_module :: WebRTC

@typedoc """
Stores information about the peer.
Expand All @@ -35,21 +48,40 @@ defmodule Jellyfish.Peer do
type: type()
}

@valid_type_strings ["webrtc"]

@doc false
@spec from_json(map()) :: t()
def from_json(response) do
case response do
%{
"id" => id,
"type" => type
"type" => type_str
} ->
%__MODULE__{
id: id,
type: type
type: type_from_string(type_str)
}

_other ->
raise StructureError
end
end

@doc false
@spec type_from_options(struct()) :: type()
def type_from_options(peer) do
case peer do
%WebRTC{} -> :webrtc
_other -> raise "Invalid peer options struct"
end
end

@doc false
@spec type_from_string(String.t()) :: type()
def type_from_string(string) do
if string in @valid_type_strings,
do: String.to_atom(string),
else: raise("Invalid peer type string")
end
end
12 changes: 12 additions & 0 deletions lib/jellyfish/peer/webrtc.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Jellyfish.Peer.WebRTC do
@moduledoc """
Options for the WebRTC peer.

For the description of these options refer to [Jellyfish documentation](https://jellyfish-dev.github.io/jellyfish-docs/getting_started/peers/webrtc)
"""

@enforce_keys []
defstruct @enforce_keys ++ []

@type t :: %__MODULE__{}
end
30 changes: 21 additions & 9 deletions lib/jellyfish/room.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ defmodule Jellyfish.Room do
peers: []
}}

iex> {:ok, peer, peer_token} = Jellyfish.Room.add_peer(client, room.id, "webrtc")
iex> {:ok, peer, peer_token} = Jellyfish.Room.add_peer(client, room.id, Jellyfish.Peer.WebRTC)
{:ok,
%Jellyfish.Peer{id: "5a731f2e-f49f-4d58-8f64-16a5c09b520e", type: "webrtc"},
%Jellyfish.Peer{id: "5a731f2e-f49f-4d58-8f64-16a5c09b520e", type: :webrtc},
"3LTQ3ZDEtYTRjNy0yZDQyZjU1MDAxY2FkAAdyb29tX2lkbQAAACQ0M"}

iex> :ok = Jellyfish.Room.delete(client, room.id)
Expand Down Expand Up @@ -127,14 +127,16 @@ defmodule Jellyfish.Room do
@doc """
Add a peer to the room with `room_id`.
"""
@spec add_peer(Client.t(), id(), Peer.type()) ::
@spec add_peer(Client.t(), id(), Peer.options() | Peer.options_module()) ::
{:ok, Peer.t(), peer_token()} | {:error, atom() | String.t()}
def add_peer(client, room_id, type) do
def add_peer(client, room_id, peer) do
peer = if is_atom(peer), do: struct!(peer), else: peer

with {:ok, %Env{status: 201, body: body}} <-
Tesla.post(
client.http_client,
"/room/#{room_id}/peer",
%{"type" => type}
%{"type" => Peer.type_from_options(peer) |> Atom.to_string()}
),
{:ok, %{"peer" => peer, "token" => token}} <- Map.fetch(body, "data"),
result <- Peer.from_json(peer) do
Expand Down Expand Up @@ -162,16 +164,20 @@ defmodule Jellyfish.Room do
@doc """
Add component to the room with `room_id`.
"""
@spec add_component(Client.t(), id(), Component.type(), Component.options()) ::
@spec add_component(Client.t(), id(), Component.options() | Component.options_module()) ::
{:ok, Component.t()} | {:error, atom() | String.t()}
def add_component(client, room_id, type, opts \\ []) do
def add_component(client, room_id, component) do
component = if is_atom(component), do: struct!(component), else: component

with {:ok, %Env{status: 201, body: body}} <-
Tesla.post(
client.http_client,
"/room/#{room_id}/component",
%{
"type" => type,
"options" => Map.new(opts)
"type" => Component.type_from_options(component) |> Atom.to_string(),
"options" =>
Map.from_struct(component)
|> Map.new(fn {k, v} -> {snake_case_to_camel_case(k), v} end)
}
),
{:ok, data} <- Map.fetch(body, "data"),
Expand Down Expand Up @@ -224,4 +230,10 @@ defmodule Jellyfish.Room do

defp handle_response_error({:ok, %Env{body: _body}}), do: raise(StructureError)
defp handle_response_error({:error, reason}), do: {:error, reason}

defp snake_case_to_camel_case(atom) do
[first | rest] = Atom.to_string(atom) |> String.split("_")
rest = rest |> Enum.map(&String.capitalize/1)
Enum.join([first | rest])
end
end
Loading