@@ -3,28 +3,107 @@ defmodule Shinkai.Config do
33
44 use GenServer
55
6- @ top_level_keys [ :rtmp , :server , :hls ]
6+ @ top_level_keys [ :rtmp , :server , :hls , :rtsp ]
77
8- @ default_config [
9- rtmp: [
10- enabled: true ,
11- port: 1935
8+ @ rtmp_schema [
9+ enabled: [
10+ type: :boolean ,
11+ default: true ,
12+ doc: "Enable or disable rtmp"
1213 ] ,
13- server: [
14- enabled: true ,
15- port: 8888 ,
16- certfile: nil ,
17- keyfile: nil
14+ port: [
15+ type: { :in , 0 .. ( 2 ** 16 - 1 ) } ,
16+ default: 1935 ,
17+ doc: "RTMP listening port" ,
18+ type_doc: "`t::socket.port_number/0`" ,
19+ type_spec: quote ( do: :socket . port_number ( ) )
20+ ]
21+ ]
22+
23+ @ rtsp_schema [
24+ enabled: [
25+ type: :boolean ,
26+ default: true ,
27+ doc: "Enable or disable rtsp"
28+ ] ,
29+ port: [
30+ type: { :in , 0 .. ( 2 ** 16 - 1 ) } ,
31+ default: 8554 ,
32+ doc: "RTSP listening port" ,
33+ type_doc: "`t::socket.port_number/0`" ,
34+ type_spec: quote ( do: :socket . port_number ( ) )
35+ ]
36+ ]
37+
38+ @ server_schema [
39+ enabled: [
40+ type: :boolean ,
41+ default: true ,
42+ doc: "Enable or disable http(s) server"
43+ ] ,
44+ port: [
45+ type: { :in , 0 .. ( 2 ** 16 - 1 ) } ,
46+ default: 8888 ,
47+ doc: "http port" ,
48+ type_doc: "`t::socket.port_number/0`" ,
49+ type_spec: quote ( do: :socket . port_number ( ) )
50+ ] ,
51+ certfile: [
52+ type: { :or , [ :string , nil ] } ,
53+ default: nil ,
54+ doc: "https certificate"
55+ ] ,
56+ keyfile: [
57+ type: { :or , [ :string , nil ] } ,
58+ default: nil ,
59+ doc: "https private key certificate"
60+ ]
61+ ]
62+
63+ @ hls_schema [
64+ storage_dir: [
65+ type: :string ,
66+ default: "/tmp/shinkai/hls" ,
67+ doc: "Directory to store HLS segments"
68+ ] ,
69+ max_segments: [
70+ type: :non_neg_integer ,
71+ default: 7 ,
72+ doc: "Max segments to keep in live playlists"
73+ ] ,
74+ segment_duration: [
75+ type: :non_neg_integer ,
76+ default: 2000 ,
77+ doc: "Segment duration in milliseconds"
1878 ] ,
19- hls: [
20- storage_dir: "/tmp/shinkai/hls" ,
21- max_segments: 7 ,
22- segment_duration: 2_000 ,
23- part_duration: 500 ,
24- segment_type: :fmp4
79+ part_duration: [
80+ type: :non_neg_integer ,
81+ default: 300 ,
82+ doc: "Part duration in milliseconds for low-latency HLS"
83+ ] ,
84+ segment_type: [
85+ type: { :custom , __MODULE__ , :validate_hls_segment_type , [ ] } ,
86+ default: :fmp4 ,
87+ doc: "Type of segments to generate, either `:fmp4`, `:mpeg_ts` or `:low_latency`"
2588 ]
2689 ]
2790
91+ @ doc false
92+ @ spec server_schema ( ) :: keyword ( )
93+ def server_schema , do: @ server_schema
94+
95+ @ doc false
96+ @ spec rtmp_schema ( ) :: keyword ( )
97+ def rtmp_schema , do: @ rtmp_schema
98+
99+ @ doc false
100+ @ spec hls_schema ( ) :: keyword ( )
101+ def hls_schema , do: @ hls_schema
102+
103+ @ doc false
104+ @ spec rtsp_schema ( ) :: keyword ( )
105+ def rtsp_schema , do: @ rtsp_schema
106+
28107 def start_link ( config ) do
29108 GenServer . start_link ( __MODULE__ , config , name: __MODULE__ )
30109 end
@@ -48,9 +127,14 @@ defmodule Shinkai.Config do
48127
49128 app_configs = Enum . map ( @ top_level_keys , & { & 1 , Application . get_env ( :shinkai , & 1 , [ ] ) } )
50129
51- Enum . map ( @ default_config , fn { key , config } ->
130+ app_configs =
131+ @ top_level_keys
132+ |> Enum . map ( & { & 1 , [ ] } )
133+ |> Keyword . merge ( app_configs )
134+ |> parse_and_validate ( )
135+
136+ Enum . map ( app_configs , fn { key , config } ->
52137 config
53- |> Keyword . merge ( app_configs [ key ] )
54138 |> Keyword . merge ( user_config [ key ] || [ ] )
55139 |> then ( & { key , & 1 } )
56140 end )
@@ -95,129 +179,41 @@ defmodule Shinkai.Config do
95179
96180 defp parse_and_validate ( [ ] , acc ) , do: acc
97181
98- defp parse_and_validate ( [ { :hls , hls_config } | rest ] , acc ) do
99- hls_config = parse_and_validate_hls ( hls_config )
100- parse_and_validate ( rest , [ { :hls , hls_config } | acc ] )
101- end
102-
103- defp parse_and_validate ( [ { :server , server_config } | rest ] , acc ) do
104- server_config = parse_and_validate_server ( server_config )
105- parse_and_validate ( rest , [ { :server , server_config } | acc ] )
106- end
107-
108- defp parse_and_validate ( [ { :rtmp , rtmp_config } | rest ] , acc ) do
109- rtmp_config = parse_and_validate_rtmp ( rtmp_config )
110- parse_and_validate ( rest , [ { :rtmp , rtmp_config } | acc ] )
111- end
112-
113- defp parse_and_validate_hls ( config , acc \\ [ ] )
114-
115- defp parse_and_validate_hls ( nil , _acc ) , do: [ ]
116- defp parse_and_validate_hls ( [ ] , acc ) , do: acc
117-
118- defp parse_and_validate_hls ( config , acc ) when is_map ( config ) do
119- parse_and_validate_hls ( Map . to_list ( config ) , acc )
120- end
121-
122- defp parse_and_validate_hls ( [ { :segment_type , value } | rest ] , acc )
123- when value in [ :fmp4 , :mpeg_ts , :low_latency ] do
124- parse_and_validate_hls ( rest , [ { :segment_type , value } | acc ] )
125- end
126-
127- defp parse_and_validate_hls ( [ { "segment_type" , value } | rest ] , acc )
128- when value in [ "fmp4" , "mpeg_ts" , "low_latency" ] do
129- parse_and_validate_hls ( rest , [ { :segment_type , String . to_atom ( value ) } | acc ] )
130- end
131-
132- defp parse_and_validate_hls ( [ { key , value } | rest ] , acc )
133- when key in [ "segment_duration" , :segment_duration ] and is_integer ( value ) and value >= 1000 do
134- parse_and_validate_hls ( rest , [ { :segment_duration , value } | acc ] )
135- end
136-
137- defp parse_and_validate_hls ( [ { key , value } | rest ] , acc )
138- when key in [ "max_segments" , :max_segments ] and is_integer ( value ) and value > 3 do
139- parse_and_validate_hls ( rest , [ { :max_segments , value } | acc ] )
140- end
141-
142- defp parse_and_validate_hls ( [ { key , value } | rest ] , acc )
143- when key in [ "part_duration" , :part_duration ] and is_integer ( value ) and value >= 100 and
144- value < 1000 do
145- parse_and_validate_hls ( rest , [ { :part_duration , value } | acc ] )
146- end
147-
148- defp parse_and_validate_hls ( [ { key , value } | rest ] , acc )
149- when key in [ "storage_dir" , :storage_dir ] do
150- parse_and_validate_hls ( rest , [ { :storage_dir , value } | acc ] )
151- end
152-
153- defp parse_and_validate_hls ( [ { key , value } | _rest ] , _acc ) do
154- raise ArgumentError , """
155- Invalid HLS configuration key or value detected.
156- Key: #{ inspect ( key ) } , Value: #{ inspect ( value ) } .
157- """
158- end
182+ defp parse_and_validate ( [ { key , config } | rest ] , acc ) do
183+ config =
184+ case key do
185+ :hls -> do_parse_and_validate ( config , @ hls_schema )
186+ :server -> do_parse_and_validate ( config , @ server_schema )
187+ :rtmp -> do_parse_and_validate ( config , @ rtmp_schema )
188+ :rtsp -> do_parse_and_validate ( config , @ rtsp_schema )
189+ end
159190
160- defp parse_and_validate_hls ( config , _acc ) do
161- raise ArgumentError , """
162- Invalid HLS configuration format detected.
163- Config: #{ inspect ( config ) } .
164- """
191+ parse_and_validate ( rest , [ { key , config } | acc ] )
165192 end
166193
167- # HTTP server
168- defp parse_and_validate_server ( config , acc \\ [ ] )
169- defp parse_and_validate_server ( nil , _acc ) , do: [ ]
170- defp parse_and_validate_server ( [ ] , acc ) , do: acc
194+ defp do_parse_and_validate ( config , schema ) do
195+ config = config || [ ]
171196
172- defp parse_and_validate_server ( config , acc ) when is_map ( config ) do
173- parse_and_validate_server ( Map . to_list ( config ) , acc )
174- end
175-
176- defp parse_and_validate_server ( [ { key , value } | rest ] , acc )
177- when key in [ "enabled" , :enabled ] and is_boolean ( value ) do
178- parse_and_validate_server ( rest , [ { :enabled , value } | acc ] )
179- end
180-
181- defp parse_and_validate_server ( [ { key , value } | rest ] , acc )
182- when key in [ :port , "port" ] and is_integer ( value ) and value > 0 and value < 65_536 do
183- parse_and_validate_server ( rest , [ { :port , value } | acc ] )
184- end
185-
186- defp parse_and_validate_server ( [ { key , value } | rest ] , acc )
187- when key in [ "certfile" , "keyfile" , :certfile , :keyfile ] do
188- parse_and_validate_server ( rest , [ { String . to_atom ( key ) , value } | acc ] )
189- end
197+ cond do
198+ Keyword . keyword? ( config ) ->
199+ NimbleOptions . validate! ( config , schema )
190200
191- defp parse_and_validate_server ( [ { key , value } | _rest ] , _acc ) do
192- raise ArgumentError , """
193- Invalid Server configuration key or value detected.
194- Key: #{ inspect ( key ) } , Value: #{ inspect ( value ) } .
195- """
196- end
201+ is_map ( config ) ->
202+ config
203+ |> Keyword . new ( fn { k , v } -> { String . to_existing_atom ( k ) , v } end )
204+ |> NimbleOptions . validate! ( schema )
197205
198- # RTMP
199- defp parse_and_validate_rtmp ( config , acc \\ [ ] )
200- defp parse_and_validate_rtmp ( nil , _acc ) , do: [ ]
201- defp parse_and_validate_rtmp ( [ ] , acc ) , do: acc
202-
203- defp parse_and_validate_rtmp ( config , acc ) when is_map ( config ) do
204- parse_and_validate_rtmp ( Map . to_list ( config ) , acc )
205- end
206-
207- defp parse_and_validate_rtmp ( [ { key , value } | rest ] , acc )
208- when key in [ "enabled" , :enabled ] and is_boolean ( value ) do
209- parse_and_validate_rtmp ( rest , [ { :enabled , value } | acc ] )
210- end
211-
212- defp parse_and_validate_rtmp ( [ { key , value } | rest ] , acc )
213- when key in [ :port , "port" ] and is_integer ( value ) and value > 0 and value < 65_536 do
214- parse_and_validate_rtmp ( rest , [ { :port , value } | acc ] )
206+ true ->
207+ raise ArgumentError , "Expected a map or keyword list received: #{ inspect ( config ) } "
208+ end
215209 end
216210
217- defp parse_and_validate_rtmp ( [ { key , value } | _rest ] , _acc ) do
218- raise ArgumentError , """
219- Invalid RTMP configuration key or value detected.
220- Key: #{ inspect ( key ) } , Value: #{ inspect ( value ) } .
221- """
211+ @ doc false
212+ def validate_hls_segment_type ( value ) do
213+ cond do
214+ value in [ :mpeg_ts , :fmp4 , :low_latency ] -> { :ok , value }
215+ value in [ "mpeg_ts" , "fmp4" , "low_latency" ] -> { :ok , String . to_atom ( value ) }
216+ true -> { :error , value }
217+ end
222218 end
223219end
0 commit comments