From 07d1d1733b1b8ad3547ffcf1d2ec5fdced728a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Tue, 16 Jan 2024 14:20:50 +0100 Subject: [PATCH 1/3] Added trak strukt --- lib/jellyfish/component.ex | 13 +++++-- lib/jellyfish/peer.ex | 13 +++++-- lib/jellyfish/room.ex | 8 ++-- lib/jellyfish/track.ex | 78 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 lib/jellyfish/track.ex diff --git a/lib/jellyfish/component.ex b/lib/jellyfish/component.ex index c46d8a4..d920f63 100644 --- a/lib/jellyfish/component.ex +++ b/lib/jellyfish/component.ex @@ -8,11 +8,13 @@ defmodule Jellyfish.Component do alias Jellyfish.Component.{File, HLS, RTSP} alias Jellyfish.Exception.StructureError + alias Jellyfish.Track @enforce_keys [ :id, :type, - :properties + :properties, + :tracks ] defstruct @enforce_keys @@ -37,7 +39,8 @@ defmodule Jellyfish.Component do @type t :: %__MODULE__{ id: id(), type: type(), - properties: map() + properties: map(), + tracks: [Track.t()] } @doc false @@ -47,14 +50,16 @@ defmodule Jellyfish.Component do %{ "id" => id, "type" => type_str, - "properties" => properties + "properties" => properties, + "tracks" => tracks } -> type = type_from_string(type_str) %__MODULE__{ id: id, type: type, - properties: type.properties_from_json(properties) + properties: type.properties_from_json(properties), + tracks: Enum.map(tracks, &Track.from_json/1) } _other -> diff --git a/lib/jellyfish/peer.ex b/lib/jellyfish/peer.ex index 12a8ba1..9de61d8 100644 --- a/lib/jellyfish/peer.ex +++ b/lib/jellyfish/peer.ex @@ -9,11 +9,13 @@ defmodule Jellyfish.Peer do alias Jellyfish.Exception.StructureError alias Jellyfish.Peer.WebRTC + alias Jellyfish.Track @enforce_keys [ :id, :type, - :status + :status, + :tracks ] defstruct @enforce_keys @@ -45,7 +47,8 @@ defmodule Jellyfish.Peer do @type t :: %__MODULE__{ id: id(), type: type(), - status: status() + status: status(), + tracks: [Track.t()] } @doc false @@ -55,12 +58,14 @@ defmodule Jellyfish.Peer do %{ "id" => id, "type" => type_str, - "status" => status_str + "status" => status_str, + "tracks" => tracks } -> %__MODULE__{ id: id, type: type_from_string(type_str), - status: status_from_string(status_str) + status: status_from_string(status_str), + tracks: Enum.map(tracks, &Track.from_json/1) } _other -> diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index deb2b9b..2d0e9d4 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -16,14 +16,16 @@ defmodule Jellyfish.Room do ...> config: %{max_peers: 10, video_codec: nil}, ...> peers: []} true - iex> assert {:ok,%Jellyfish.Peer{ + iex> assert {:ok, %Jellyfish.Peer{ ...> status: :disconnected, - ...> type: Jellyfish.Peer.WebRTC + ...> type: Jellyfish.Peer.WebRTC, + ...> tracks: [] ...> } = peer, _peer_token} = Jellyfish.Room.add_peer(client, room.id, Jellyfish.Peer.WebRTC) iex> %Jellyfish.Peer{ ...> id: peer.id, ...> status: :disconnected, - ...> type: Jellyfish.Peer.WebRTC} == peer + ...> type: Jellyfish.Peer.WebRTC, + ...> tracks: []} == peer true iex> :ok = Jellyfish.Room.delete(client, room.id) :ok diff --git a/lib/jellyfish/track.ex b/lib/jellyfish/track.ex new file mode 100644 index 0000000..77af008 --- /dev/null +++ b/lib/jellyfish/track.ex @@ -0,0 +1,78 @@ +defmodule Jellyfish.Track do + @moduledoc """ + Defines `t:Jellyfish.Track.t/0`. + + It represents a single media track, either audio or video. + """ + + alias Jellyfish.Exception.StructureError + + @enforce_keys [ + :id, + :type, + :encoding + ] + defstruct @enforce_keys ++ [metadata: %{}] + + @typedoc """ + Id of the track, unique within the room. + """ + @type id :: String.t() + + @typedoc """ + Type of the track. + """ + @type type :: :audio | :video + + @valid_type_string ["audio", "video"] + + @typedoc """ + Encoding of the track. + """ + @type encoding :: :H264 | :VP8 | :OPUS + + @valid_encoding_strings ["H264", "VP8", "OPUS"] + + @typedoc """ + Track metadata. + """ + @type metadata :: String.t() + + @typedoc """ + Stores information about the track. + """ + @type t :: %__MODULE__{ + id: id(), + type: type(), + encoding: encoding(), + metadata: metadata() + } + + @doc false + @spec from_json(map()) :: t() + def from_json(response) do + case response do + %{ + "id" => id, + "type" => type_str, + "encoding" => encoding_str, + "metadata" => metadata + } -> + %__MODULE__{ + id: id, + type: type_from_string(type_str), + encoding: encoding_from_string(encoding_str), + metadata: metadata + } + + _other -> + raise StructureError + end + end + + defp type_from_string(type) when type in @valid_type_string, + do: String.to_atom(type) + + def encoding_from_string(encoding) when encoding in @valid_encoding_strings, + do: String.to_atom(encoding) +end From e63a00820ad410ac04a98a9379b79599081a010c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Fri, 2 Feb 2024 19:04:10 +0100 Subject: [PATCH 2/3] Add peer metadata --- lib/jellyfish/component.ex | 4 +- lib/jellyfish/component/file.ex | 3 +- lib/jellyfish/notification.ex | 135 +++++++++++++++++- lib/jellyfish/peer.ex | 12 +- lib/jellyfish/room.ex | 3 +- lib/jellyfish/track.ex | 24 +--- lib/jellyfish/webhook_notifier.ex | 11 +- .../jellyfish/server_notifications.pb.ex | 89 ++++++++++++ test/jellyfish/notifier_test.exs | 117 +++++++++++---- test/support/webhook_plug.ex | 4 +- 10 files changed, 333 insertions(+), 69 deletions(-) diff --git a/lib/jellyfish/component.ex b/lib/jellyfish/component.ex index d920f63..cbf7d85 100644 --- a/lib/jellyfish/component.ex +++ b/lib/jellyfish/component.ex @@ -26,12 +26,12 @@ defmodule Jellyfish.Component do @typedoc """ Type of the component. """ - @type type :: HLS | RTSP + @type type :: HLS | RTSP | File @typedoc """ Component-specific options. """ - @type options :: HLS.t() | RTSP.t() + @type options :: HLS.t() | RTSP.t() | File.t() @typedoc """ Stores information about the component. diff --git a/lib/jellyfish/component/file.ex b/lib/jellyfish/component/file.ex index 564000d..f105e3d 100644 --- a/lib/jellyfish/component/file.ex +++ b/lib/jellyfish/component/file.ex @@ -9,8 +9,7 @@ defmodule Jellyfish.Component.File do @behaviour Jellyfish.Component.Deserializer @enforce_keys [:file_path] - defstruct @enforce_keys ++ - [] + defstruct @enforce_keys @type t :: %__MODULE__{ file_path: String.t() diff --git a/lib/jellyfish/notification.ex b/lib/jellyfish/notification.ex index 34b6379..0874347 100644 --- a/lib/jellyfish/notification.ex +++ b/lib/jellyfish/notification.ex @@ -1,7 +1,8 @@ defmodule Jellyfish.Notification do @moduledoc false - alias Jellyfish.{Component, Peer, Room} + alias Jellyfish.{Component, Peer, Room, Track} + alias Jellyfish.ServerMessage.{TrackAdded, TrackMetadataUpdated, TrackRemoved} defmodule RoomCreated do @moduledoc nil @@ -60,6 +61,19 @@ defmodule Jellyfish.Notification do } end + defmodule PeerMetadataUpdated do + @moduledoc nil + + @enforce_keys [:room_id, :peer_id, :metadata] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + peer_id: Peer.id(), + metadata: any() + } + end + defmodule PeerCrashed do @moduledoc nil @@ -118,9 +132,117 @@ defmodule Jellyfish.Notification do } end + defmodule ComponentTrackAdded do + @moduledoc nil + + @enforce_keys [:room_id, :component_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + component_id: Peer.id(), + track: Track.t() + } + end + + defmodule ComponentTrackMetadataUpdated do + @moduledoc nil + + @enforce_keys [:room_id, :component_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + component_id: Component.id(), + track: Track.t() + } + end + + defmodule ComponentTrackRemoved do + @moduledoc nil + + @enforce_keys [:room_id, :component_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + component_id: Component.id(), + track: Track.t() + } + end + + defmodule PeerTrackAdded do + @moduledoc nil + + @enforce_keys [:room_id, :peer_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + peer_id: Peer.id(), + track: Track.t() + } + end + + defmodule PeerTrackMetadataUpdated do + @moduledoc nil + + @enforce_keys [:room_id, :peer_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + peer_id: Peer.id(), + track: Track.t() + } + end + + defmodule PeerTrackRemoved do + @moduledoc nil + + @enforce_keys [:room_id, :peer_id, :track] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + room_id: Room.id(), + peer_id: Peer.id(), + track: Track.t() + } + end + @discarded_fields [:__unknown_fields__] @doc false + def to_notification(%module{} = message) + when module in [TrackAdded, TrackMetadataUpdated, TrackRemoved] do + {endpoint_type, endpoint_id} = message.endpoint_info + + endpoint_type_str = + endpoint_type + |> Atom.to_string() + |> String.split("_") + |> List.first() + |> String.capitalize() + + notification_module = + module + |> Module.split() + |> List.last() + |> then(&(endpoint_type_str <> &1)) + |> then(&Module.concat(__MODULE__, &1)) + + discarded_fields = @discarded_fields ++ [:endpoint_info] + + message + |> Map.from_struct() + |> Map.drop(discarded_fields) + |> Map.merge(%{ + endpoint_type => endpoint_id, + :track => from_proto_track(message.track) + }) + |> then(&struct!(notification_module, &1)) + end + def to_notification(%module{} = message) do notification_module = module @@ -133,4 +255,15 @@ defmodule Jellyfish.Notification do |> Map.drop(@discarded_fields) |> then(&struct!(notification_module, &1)) end + + defp from_proto_track(track) do + %Track{ + id: track.id, + type: from_proto_track_type(track.type), + metadata: Jason.decode!(track.metadata) + } + end + + defp from_proto_track_type(:TRACK_TYPE_VIDEO), do: :video + defp from_proto_track_type(:TRACK_TYPE_AUDIO), do: :audio end diff --git a/lib/jellyfish/peer.ex b/lib/jellyfish/peer.ex index 9de61d8..969866d 100644 --- a/lib/jellyfish/peer.ex +++ b/lib/jellyfish/peer.ex @@ -15,7 +15,8 @@ defmodule Jellyfish.Peer do :id, :type, :status, - :tracks + :tracks, + :metadata ] defstruct @enforce_keys @@ -48,7 +49,8 @@ defmodule Jellyfish.Peer do id: id(), type: type(), status: status(), - tracks: [Track.t()] + tracks: [Track.t()], + metadata: any() } @doc false @@ -59,13 +61,15 @@ defmodule Jellyfish.Peer do "id" => id, "type" => type_str, "status" => status_str, - "tracks" => tracks + "tracks" => tracks, + "metadata" => metadata } -> %__MODULE__{ id: id, type: type_from_string(type_str), status: status_from_string(status_str), - tracks: Enum.map(tracks, &Track.from_json/1) + tracks: Enum.map(tracks, &Track.from_json/1), + metadata: metadata } _other -> diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index 2d0e9d4..512fb69 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -25,7 +25,8 @@ defmodule Jellyfish.Room do ...> id: peer.id, ...> status: :disconnected, ...> type: Jellyfish.Peer.WebRTC, - ...> tracks: []} == peer + ...> tracks: [], + ...> metadata: nil} == peer true iex> :ok = Jellyfish.Room.delete(client, room.id) :ok diff --git a/lib/jellyfish/track.ex b/lib/jellyfish/track.ex index 77af008..a875d3c 100644 --- a/lib/jellyfish/track.ex +++ b/lib/jellyfish/track.ex @@ -10,9 +10,9 @@ defmodule Jellyfish.Track do @enforce_keys [ :id, :type, - :encoding + :metadata ] - defstruct @enforce_keys ++ [metadata: %{}] + defstruct @enforce_keys @typedoc """ Id of the track, unique within the room. @@ -26,26 +26,13 @@ defmodule Jellyfish.Track do @valid_type_string ["audio", "video"] - @typedoc """ - Encoding of the track. - """ - @type encoding :: :H264 | :VP8 | :OPUS - - @valid_encoding_strings ["H264", "VP8", "OPUS"] - - @typedoc """ - Track metadata. - """ - @type metadata :: String.t() - @typedoc """ Stores information about the track. """ @type t :: %__MODULE__{ id: id(), type: type(), - encoding: encoding(), - metadata: metadata() + metadata: any() } @doc false @@ -55,13 +42,11 @@ defmodule Jellyfish.Track do %{ "id" => id, "type" => type_str, - "encoding" => encoding_str, "metadata" => metadata } -> %__MODULE__{ id: id, type: type_from_string(type_str), - encoding: encoding_from_string(encoding_str), metadata: metadata } @@ -72,7 +57,4 @@ defmodule Jellyfish.Track do defp type_from_string(type) when type in @valid_type_string, do: String.to_atom(type) - - def encoding_from_string(encoding) when encoding in @valid_encoding_strings, - do: String.to_atom(encoding) end diff --git a/lib/jellyfish/webhook_notifier.ex b/lib/jellyfish/webhook_notifier.ex index 6e0ee78..79f1ae2 100644 --- a/lib/jellyfish/webhook_notifier.ex +++ b/lib/jellyfish/webhook_notifier.ex @@ -9,13 +9,12 @@ defmodule Jellyfish.WebhookNotifier do Decodes received webhook to notification structs. ``` - iex> %{ - ...> "notification" => <<18, 76, 10, 36, 102, 98, 102, 52, 49, 57, 48, 99, 45, 53, 99, 55, 54, 45, 52, + iex> <<18, 76, 10, 36, 102, 98, 102, 52, 49, 57, 48, 99, 45, 53, 99, 55, 54, 45, 52, ...> 49, 53, 99, 45, 56, 57, 51, 57, 45, 53, 50, 99, 54, 101, 100, 50, 48, 56, 54, ...> 56, 98, 18, 36, 99, 55, 50, 51, 54, 53, 56, 55, 45, 53, 100, 102, 56, 45, 52, ...> 98, 52, 49, 45, 98, 54, 101, 52, 45, 50, 54, 56, 101, 55, 49, 49, 51, 51, 101, ...> 101, 50>> - ...> } |> Jellyfish.WebhookNotifier.receive() + ...> |> Jellyfish.WebhookNotifier.receive() %Jellyfish.Notification.PeerConnected{ room_id: "fbf4190c-5c76-415c-8939-52c6ed20868b", peer_id: "c7236587-5df8-4b41-b6e4-268e71133ee2" @@ -23,11 +22,9 @@ defmodule Jellyfish.WebhookNotifier do ``` """ @spec receive(term()) :: struct() - def receive(json) do + def receive(binary) do %ServerMessage{content: {_type, notification}} = - json - |> Map.get("notification") - |> ServerMessage.decode() + ServerMessage.decode(binary) Notification.to_notification(notification) end diff --git a/lib/protos/jellyfish/server_notifications.pb.ex b/lib/protos/jellyfish/server_notifications.pb.ex index 2dc99ad..5348b6c 100644 --- a/lib/protos/jellyfish/server_notifications.pb.ex +++ b/lib/protos/jellyfish/server_notifications.pb.ex @@ -8,6 +8,16 @@ defmodule Jellyfish.ServerMessage.EventType do field :EVENT_TYPE_METRICS, 2 end +defmodule Jellyfish.ServerMessage.TrackType do + @moduledoc false + + use Protobuf, enum: true, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + field :TRACK_TYPE_UNSPECIFIED, 0 + field :TRACK_TYPE_VIDEO, 1 + field :TRACK_TYPE_AUDIO, 2 +end + defmodule Jellyfish.ServerMessage.RoomCrashed do @moduledoc false @@ -137,6 +147,65 @@ defmodule Jellyfish.ServerMessage.HlsUploadCrashed do field :room_id, 1, type: :string, json_name: "roomId" end +defmodule Jellyfish.ServerMessage.PeerMetadataUpdated do + @moduledoc false + + use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + field :room_id, 1, type: :string, json_name: "roomId" + field :peer_id, 2, type: :string, json_name: "peerId" + field :metadata, 3, type: :string +end + +defmodule Jellyfish.ServerMessage.Track do + @moduledoc false + + use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + field :id, 1, type: :string + field :type, 2, type: Jellyfish.ServerMessage.TrackType, enum: true + field :metadata, 3, type: :string +end + +defmodule Jellyfish.ServerMessage.TrackAdded do + @moduledoc false + + use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + oneof :endpoint_info, 0 + + field :room_id, 1, type: :string, json_name: "roomId" + field :peer_id, 2, type: :string, json_name: "peerId", oneof: 0 + field :component_id, 3, type: :string, json_name: "componentId", oneof: 0 + field :track, 4, type: Jellyfish.ServerMessage.Track +end + +defmodule Jellyfish.ServerMessage.TrackRemoved do + @moduledoc false + + use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + oneof :endpoint_info, 0 + + field :room_id, 1, type: :string, json_name: "roomId" + field :peer_id, 2, type: :string, json_name: "peerId", oneof: 0 + field :component_id, 3, type: :string, json_name: "componentId", oneof: 0 + field :track, 4, type: Jellyfish.ServerMessage.Track +end + +defmodule Jellyfish.ServerMessage.TrackMetadataUpdated do + @moduledoc false + + use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.12.0" + + oneof :endpoint_info, 0 + + field :room_id, 1, type: :string, json_name: "roomId" + field :peer_id, 2, type: :string, json_name: "peerId", oneof: 0 + field :component_id, 3, type: :string, json_name: "componentId", oneof: 0 + field :track, 4, type: Jellyfish.ServerMessage.Track +end + defmodule Jellyfish.ServerMessage do @moduledoc false @@ -215,4 +284,24 @@ defmodule Jellyfish.ServerMessage do type: Jellyfish.ServerMessage.HlsUploadCrashed, json_name: "hlsUploadCrashed", oneof: 0 + + field :peer_metadata_updated, 16, + type: Jellyfish.ServerMessage.PeerMetadataUpdated, + json_name: "peerMetadataUpdated", + oneof: 0 + + field :track_added, 17, + type: Jellyfish.ServerMessage.TrackAdded, + json_name: "trackAdded", + oneof: 0 + + field :track_removed, 18, + type: Jellyfish.ServerMessage.TrackRemoved, + json_name: "trackRemoved", + oneof: 0 + + field :track_metadata_updated, 19, + type: Jellyfish.ServerMessage.TrackMetadataUpdated, + json_name: "trackMetadataUpdated", + oneof: 0 end diff --git a/test/jellyfish/notifier_test.exs b/test/jellyfish/notifier_test.exs index f387822..36ac112 100644 --- a/test/jellyfish/notifier_test.exs +++ b/test/jellyfish/notifier_test.exs @@ -1,14 +1,18 @@ defmodule Jellyfish.NotifierTest do use ExUnit.Case doctest Jellyfish.WebhookNotifier - alias Jellyfish.{Client, Peer, Room, WSNotifier} + alias Jellyfish.{Client, Component, Peer, Room, Track, WSNotifier} + alias Jellyfish.Component.File alias Jellyfish.PeerMessage alias Jellyfish.PeerMessage.AuthRequest alias Jellyfish.Notification.{ + ComponentTrackAdded, + ComponentTrackRemoved, PeerConnected, PeerDisconnected, + PeerMetadataUpdated, RoomCreated, RoomDeleted } @@ -20,6 +24,10 @@ defmodule Jellyfish.NotifierTest do @peer_opts %Peer.WebRTC{} + @file_component_opts %File{ + file_path: "video.h264" + } + @max_peers 10 @video_codec :vp8 @webhook_port 4000 @@ -88,31 +96,78 @@ defmodule Jellyfish.NotifierTest do assert_receive {:webhook, %RoomDeleted{room_id: ^room_id}}, 2_500 end - test "when peer connects and then disconnects", %{ + test "when peer connects, updates metadata and then disconnects", %{ client: client } do - {:ok, %Jellyfish.Room{id: room_id}, jellyfish_address} = - Room.create(client, - max_peers: @max_peers, - video_codec: @video_codec, - webhook_url: @webhook_address - ) + {room_id, peer_id, peer_ws} = create_room_and_auth_ws(client) - {:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Room.add_peer(client, room_id, @peer_opts) + assert_receive {:jellyfish, + %PeerConnected{peer_id: ^peer_id, room_id: ^room_id} = peer_connected} - {:ok, peer_ws} = WS.start_link("ws://#{jellyfish_address}/socket/peer/websocket") + assert_receive {:webhook, ^peer_connected}, 2_500 - auth_request = %PeerMessage{content: {:auth_request, %AuthRequest{token: peer_token}}} + metadata = %{name: "GelatinGenius"} + metadata_encoded = Jason.encode!(metadata) - :ok = WS.send_frame(peer_ws, auth_request) + media_event = %PeerMessage{ + content: + {:media_event, + %PeerMessage.MediaEvent{ + data: %{"type" => "connect", "data" => %{"metadata" => metadata}} |> Jason.encode!() + }} + } - assert_receive {:jellyfish, %PeerConnected{peer_id: ^peer_id, room_id: ^room_id}} - assert_receive {:webhook, %PeerConnected{peer_id: ^peer_id, room_id: ^room_id}}, 2_500 + :ok = WS.send_frame(peer_ws, media_event) + + assert_receive {:jellyfish, + %PeerMetadataUpdated{ + peer_id: ^peer_id, + room_id: ^room_id, + metadata: ^metadata_encoded + } = peer_metadata_updated}, + 2000 + + assert_receive {:webhook, ^peer_metadata_updated}, 2_500 :ok = Room.delete_peer(client, room_id, peer_id) - assert_receive {:jellyfish, %PeerDisconnected{peer_id: ^peer_id, room_id: ^room_id}}, 1_000 - assert_receive {:webhook, %PeerDisconnected{peer_id: ^peer_id, room_id: ^room_id}}, 2_500 + assert_receive {:jellyfish, + %PeerDisconnected{peer_id: ^peer_id, room_id: ^room_id} = peer_disconnected}, + 1_000 + + assert_receive {:webhook, ^peer_disconnected}, 2_500 + end + + @tag :file_component_sources + test "TrackAdded and TrackRemoved are sent when adding and removing FileComponent", %{ + client: client + } do + {room_id, _peer_id, _peer_ws} = create_room_and_auth_ws(client, video_codec: :h264) + + {:ok, %Component{id: component_id}} = + Room.add_component(client, room_id, @file_component_opts) + + assert_receive {:jellyfish, + %ComponentTrackAdded{ + room_id: ^room_id, + component_id: ^component_id, + track: %Track{id: _track_id, type: :video, metadata: nil} = track + } = component_track_added}, + 500 + + assert_receive {:webhook, ^component_track_added} + + :ok = Room.delete_component(client, room_id, component_id) + + assert_receive {:jellyfish, + %ComponentTrackRemoved{ + room_id: ^room_id, + component_id: ^component_id, + track: ^track + } = component_track_removed}, + 1000 + + assert_receive {:webhook, ^component_track_removed} end end @@ -126,18 +181,7 @@ defmodule Jellyfish.NotifierTest do end test "with one peer", %{client: client} do - {:ok, %Jellyfish.Room{id: room_id}, jellyfish_address} = - Room.create(client, - max_peers: @max_peers, - webhook_url: @webhook_address - ) - - {:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Room.add_peer(client, room_id, @peer_opts) - - {:ok, peer_ws} = WS.start_link("ws://#{jellyfish_address}/socket/peer/websocket") - - auth_request = %PeerMessage{content: {:auth_request, %AuthRequest{token: peer_token}}} - :ok = WS.send_frame(peer_ws, auth_request) + {room_id, peer_id, _peer_ws} = create_room_and_auth_ws(client) assert_receive {:jellyfish, %PeerConnected{peer_id: ^peer_id, room_id: ^room_id}} assert_receive {:webhook, %PeerConnected{peer_id: ^peer_id, room_id: ^room_id}}, 2_500 @@ -145,4 +189,21 @@ defmodule Jellyfish.NotifierTest do assert_receive {:jellyfish, %MetricsReport{metrics: metrics}} when metrics != %{}, 1500 end end + + defp create_room_and_auth_ws(client, room_opts \\ []) do + {:ok, %Jellyfish.Room{id: room_id}, jellyfish_address} = + Room.create(client, + max_peers: Keyword.get(room_opts, :max_peers, @max_peers), + video_codec: Keyword.get(room_opts, :video_codec, @video_codec), + webhook_url: Keyword.get(room_opts, :webhook_url, @webhook_address) + ) + + {:ok, %Jellyfish.Peer{id: peer_id}, peer_token} = Room.add_peer(client, room_id, @peer_opts) + + {:ok, peer_ws} = WS.start_link("ws://#{jellyfish_address}/socket/peer/websocket") + + auth_request = %PeerMessage{content: {:auth_request, %AuthRequest{token: peer_token}}} + :ok = WS.send_frame(peer_ws, auth_request) + {room_id, peer_id, peer_ws} + end end diff --git a/test/support/webhook_plug.ex b/test/support/webhook_plug.ex index b45f898..605a476 100644 --- a/test/support/webhook_plug.ex +++ b/test/support/webhook_plug.ex @@ -12,9 +12,7 @@ defmodule WebHookPlug do def call(conn, _opts) do {:ok, body, conn} = Plug.Conn.read_body(conn, []) - notification = Jason.decode!(body) - - notification = WebhookNotifier.receive(notification) + notification = WebhookNotifier.receive(body) :ok = PubSub.broadcast(@pubsub, "webhook", {:webhook, notification}) From 11810e05a6171f1e8ba02819044442ca6db6d5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ro=C5=BCnawski?= Date: Tue, 6 Feb 2024 12:15:47 +0100 Subject: [PATCH 3/3] Simplify generating notifications --- lib/jellyfish/notification.ex | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/jellyfish/notification.ex b/lib/jellyfish/notification.ex index 0874347..0c6b441 100644 --- a/lib/jellyfish/notification.ex +++ b/lib/jellyfish/notification.ex @@ -140,7 +140,7 @@ defmodule Jellyfish.Notification do @type t :: %__MODULE__{ room_id: Room.id(), - component_id: Peer.id(), + component_id: Component.id(), track: Track.t() } end @@ -217,19 +217,15 @@ defmodule Jellyfish.Notification do when module in [TrackAdded, TrackMetadataUpdated, TrackRemoved] do {endpoint_type, endpoint_id} = message.endpoint_info - endpoint_type_str = - endpoint_type - |> Atom.to_string() - |> String.split("_") - |> List.first() - |> String.capitalize() - notification_module = - module - |> Module.split() - |> List.last() - |> then(&(endpoint_type_str <> &1)) - |> then(&Module.concat(__MODULE__, &1)) + case {endpoint_type, module} do + {:component, TrackAdded} -> ComponentTrackAdded + {:component, TrackMetadataUpdated} -> ComponentTrackMetadataUpdated + {:component, TrackRemoved} -> ComponentTrackRemoved + {:peer, TrackAdded} -> PeerTrackAdded + {:peer, TrackMetadataUpdated} -> PeerTrackMetadataUpdated + {:peer, TrackRemoved} -> PeerTrackRemoved + end discarded_fields = @discarded_fields ++ [:endpoint_info]