defmodule TribeOne.TribesPlugin.Sender.Plugin do @moduledoc """ Tribes plugin entry point. """ use Tribes.Plugin.Base, otp_app: :tribe_one_sender @impl true def register(context) do super(context) |> Map.merge(%{ nav_items: [ %{ label: "Streaming", path: "/sender", icon: nil, requires: [], order: 50 } ], pages: [ %{ path: "/sender", live_view: TribeOne.TribesPlugin.SenderWeb.HomeLive, layout: nil } ], children: TribeOne.TribesPlugin.Sender.Application.children(), api_routes: [ {"streams", TribeOne.TribesPlugin.SenderWeb.StreamingAPIPlug}, {"player-events", TribeOne.TribesPlugin.SenderWeb.StreamingAPIPlug} ], management_methods: [ %{ name: "capabilities", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Capabilities, action: :call, auth: :admin, description: "Describe Sender management capabilities." }, %{ name: "stream.get_default", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Stream, action: :get_default, auth: :admin, description: "Fetch the default Sender stream." }, %{ name: "stream.ensure_default", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Stream, action: :ensure_default, auth: :admin, description: "Create the default Sender stream if it does not exist." }, %{ name: "stream.update_default", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Stream, action: :update_default, auth: :admin, description: "Update the default Sender stream." }, %{ name: "stream_key.create", version: "1", module: TribeOne.TribesPlugin.Sender.Management.StreamKey, action: :create, auth: :admin, description: "Create an OBS stream key and return it once.", sensitive_response?: true }, %{ name: "media_endpoints.upsert", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Topology, action: :upsert_endpoint, auth: :admin, description: "Create or update a Sender media endpoint." }, %{ name: "renditions.upsert", version: "1", module: TribeOne.TribesPlugin.Sender.Management.Topology, action: :upsert_rendition, auth: :admin, description: "Create or update Sender rendition metadata." }, %{ name: "stream.start", version: "1", module: TribeOne.TribesPlugin.Sender.Management.StreamLifecycle, action: :start, auth: :admin, description: "Start the default stream in origin_ingest or hls_edge mode." }, %{ name: "stream.stop", version: "1", module: TribeOne.TribesPlugin.Sender.Management.StreamLifecycle, action: :stop, auth: :admin, description: "Stop the active Sender stream generation." }, %{ name: "stream.status", version: "1", module: TribeOne.TribesPlugin.Sender.Management.StreamLifecycle, action: :status, auth: :admin, description: "Return current Sender stream and session status." }, %{ name: "endpoint_snapshots.report", version: "1", module: TribeOne.TribesPlugin.Sender.Management.EndpointSnapshots, action: :report, auth: :admin, description: "Report current endpoint stats for playback routing." } ], metrics: [ %{ name: "viewer_count", stat: "max", query: "sum by (tribes_external_node_id) (max_over_time(vinyl_hls_active_viewers[60s])) or max_over_time(plugins_tribe_one_sender_viewer_count[60s])", description: "Estimated active Sender viewers on this node", event_name: [:tribe_one_sender, :metrics, :viewer_count], measurement: :value, max_series: 16 }, %{ name: "playlist_requests", stat: "sum", query: "sum by (tribes_external_node_id) (increase(vinyl_hls_playlist_requests_total[60s]))", description: "Sender HLS playlist requests during the rollup bucket", max_series: 16 }, %{ name: "hls_log_parse_errors", stat: "sum", query: "sum by (tribes_external_node_id) (increase(vinyl_hls_log_parse_errors_total[60s]))", description: "Vinyl HLS log parse errors during the rollup bucket", max_series: 16 } ], plugs: [ {"/sender/hls", TribeOne.TribesPlugin.SenderWeb.HLSPlug, []} ], hooks: %{}, ash_domains: [TribeOne.TribesPlugin.Sender.Streaming], config_schema: %{ title: "Sender", description: "RTMP ingest and HLS playback defaults for this Tribes cluster.", groups: [ %{ id: "playback", label: "Playback", description: "Browser playback and HLS latency defaults.", order: 10, settings: [ %{ key: "playback.low_latency", label: "Low latency HLS", description: "Prefer low-latency playlist behavior when available.", type: :boolean, default: true, order: 10 }, %{ key: "playback.target_duration_ms", label: "Target duration", description: "Preferred HLS segment target duration.", type: :integer, default: 2_000, min: 500, max: 10_000, step: 100, unit: "ms", order: 20 } ] }, %{ id: "player", label: "Player", description: "Default behavior for embedded Sender players.", order: 20, settings: [ %{ key: "player.autoplay", label: "Autoplay", description: "Start playback automatically when the browser allows it.", type: :boolean, default: false, order: 10 }, %{ key: "player.muted", label: "Start muted", description: "Begin playback muted by default.", type: :boolean, default: true, order: 20 } ] } ] } }) end end