develop7
6/29/2013 - 3:16 PM

Flussonic config grammar

Flussonic config grammar

% sublime: syntax Erlang


config <- config_line* ~;

% Here goes all possible top-level things
config_line <- listener / loglevel / sessions / max_sessions_option / autogenerate_tokens / include / 
  http_event / central / stream / rewrite / live / file /
  view_auth / edit_auth;



% Listener is for protocols
listener <- space? proto:("http" / "rtsp" / "rtmp") space? port:number ";"? space? `
  {proto,Proto} = lists:keyfind(proto,1,Node),
  {binary_to_atom(Proto,latin1), pl(port,Node)}
`;


loglevel <- space? "loglevel" space? level:string space? ";"? space? `
  {loglevel, binary_to_atom(pl(level,Node), latin1)}
`;

sessions <- space? ("sessions" / "auth") space? url:string space? ";"? space? `
  URL = case pl(url,Node) of
    <<"true">> -> true;
    <<"false">> -> false;
    Else -> Else
  end,
  {auth, URL, []}
`;

autogenerate_tokens <- space? "no_auto_token" space? ";"? space? `
  {autogenerate_token, false}
`;

include <- space? "include" space? path:string space? ";"? space? `
  {include, binary_to_list(pl(path,Node))}
`;


http_event <- space? "notify" space? url:string space? ";"? space? `
  {http_events, pl(url,Node)}
`;

central <- space? "central" space? key:string space? url:string space? ";"? space? /
  space? "central" space? key:string space? ";"? space? `
  Key = pl(key, Node),
  case pl(url, Node) of
    undefined -> {central, Key};
    URL -> {central, Key, [{host,URL}]}
  end
`;


% Static stream definitions
stream <- short_stream / full_stream;

short_stream <- space? "stream" space? name:string space? url:string space? ";"? space? `
  {stream, pl(name,Node), [pl(url,Node)], []}
`;

full_stream <- space? "stream" space? name:string space? "{" space? options:(stream_option*) "}" space? ";"? space? `
  apply_stream_options(pl(name,Node), pl(options,Node))
`;

stream_option <- auth_option / urls_option / url_option / dvr_option / domains_option / domain_option / udp_option / 
  thumb_option / retry_option / clients_timeout_option / password_option / push_option / publish_enabled_option /
  max_sessions_option / transcoder_option / rtp_option;
file_option <- auth_option / urls_option / url_option / domains_option / domain_option / cache_option /
  read_queue_option;



% Dynamic rewrite
rewrite <- long_rewrite / short_rewrite;

short_rewrite <- space? "rewrite" space? name:string space? url:string space? ";"? space? `
  Name1 = pl(name,Node),
  {Type,Name, Opts} = case re:run(Name1, "(.*)/\\*", [{capture,all_but_first,binary}]) of
    {match, [N1]} -> {dynamic, N1,[]};
    nomatch -> {stream, Name1, [{static,false}]}
  end,
  {Type, Name, [pl(url,Node)], Opts}
`;

long_rewrite <- space? "rewrite" space? name:string space? "{" space? options:(stream_option*) "}" space? ";"? space? `
  #opts{} = Opts = apply_options(pl(options,Node)),
  Options = make_options(Opts),

  Name1 = pl(name,Node),
  {Type,Name,Opts1} = case re:run(Name1, "(.*)/\\*", [{capture,all_but_first,binary}]) of
    {match, [N1]} -> {dynamic, N1,[]};
    nomatch -> {stream, Name1,[{static,false}]}
  end,
  {Type, Name, urls(Opts), Opts1++Options}
`;



% Live

live <- full_live / short_live;

short_live <- space? "live" space? name:string space? ";"? space? `
  {live, pl(name,Node), []}
`;

full_live <- space? "live" space? name:string space? "{" space? options:(stream_option*) "}" space? ";"? space? `
  #opts{} = Opts = apply_options(pl(options,Node)),
  {live, pl(name,Node), make_options(Opts)}
`;



% Files streaming

file <- full_file / short_file;

short_file <- space? "file" space? name:string space? path:string space? ";"? space? `
  {file, pl(name,Node), [pl(path,Node)], []}
`;

full_file <- space? "file" space? name:string space? "{" space? options:(file_option*) "}" space? ";"? space? `
  #opts{} = Opts = apply_options(pl(options,Node)),
  Name = pl(name,Node),
  {file, Name, urls(Opts), make_options(Opts#opts{prefix = Name})}
`;



% Auth of admin panel

view_auth <- space? "view_auth" space? login:string space? password:string space? ";"? space? `
  {view_auth, pl(login,Node), pl(password,Node)}
`;

edit_auth <- space? "edit_auth" space? login:string space? password:string space? ";"? space? `
  {edit_auth, pl(login,Node), pl(password,Node)}
`;






% Options


auth_option <- space? "auth" space? url:string space? ";"? space? `
  URL = case pl(url,Node) of
    <<"true">> -> true;
    <<"false">> -> false;
    URL_ -> URL_
  end,
  {auth, URL}
`;

url_option <- space? ("url" / "path") space? url:string space? ";"? space? ` 
  {url, pl(url,Node)} 
`;

urls_option <- space? ("urls" / "paths") space? urls:((s:string space?)*) ";"? space? ` 
  {urls, [pl(s,N) || N <- pl(urls,Node)]} 
`;

dvr_option <- space? "dvr" space? path:string space? settings:(dvr_setting*) space? ";"? space? `
  S = pl(settings,Node),
  {dvr, pl(path,Node), pl(dvr_limit,S), pl(disk_limit,S)}
`;

dvr_setting <- disk_limit:percent space? / dvr_limit:dvr_limit space? `
  hd(Node)
`;

dvr_limit <- t:time / n:number `
  case Node of
    {t,Time} -> Time div 60;
    {n,Time} -> Time
  end
`;

domain_option <- space? "domain" space? domain:string space? ";"? space? `
  {domain, pl(domain,Node)}
`;

domains_option <- space? "domains" space? domains:((s:string space?)*) ";"? space? `
  {domains, [pl(s,N) || N <- pl(domains,Node)]}
`;


udp_option <- space? "udp" space? url:string space? mc:"multicast_loop" space? ";"? space? /
space? "udp" space? url:string space? space? ";"? space? `
  case pl(mc,Node) of
    undefined -> {udp, pl(url,Node)};
    _ ->  {udp, pl(url,Node), multicast_loop}
  end
`;

thumb_option <- space? "thumbnails" space? ";"? space? `
  {thumb, true}
`;

cache_option <- space? "cache" space? path:string space? time_limit:number space? disk_limit:number space? ";"? space? /
space? "cache" space? path:string space? settings:(cache_setting*) ";"? space? `
  case pl(time_limit,Node) of
    undefined -> {cache, pl(path,Node), pl(settings,Node)};
    Time -> {cache, pl(path,Node), [{time_limit,Time},{disk_limit,pl(disk_limit,Node)*1024*1024}]}
  end
`;

cache_setting <- "misses=" misses:number space? / time_limit:time space? / disk_limit:disk space? `
  case lists:keyfind(misses, 1, Node) of
    {misses,M} -> {misses, M};
    false -> hd(Node)
  end
`;


retry_option <- space? "retry_limit" space? limit:number space? ";"? space? `
  {retry_limit, pl(limit,Node)}
`;

clients_timeout_option <- space? ("clients_timeout" / "client_timeout") space? timeout:number space? ";"? space? `
  {clients_timeout, pl(timeout,Node)}
`;

password_option <- space? "password" space? password:string space? ";"? space? `
  {password, binary_to_list(pl(password,Node))}
`;

push_option <- space? "push" space? url:string space? ";"? space? `
  {push, pl(url,Node)}
`;

publish_enabled_option <- space? "publish_enabled" space? ";"? space? `
  {publish_enabled, true}
`;

max_sessions_option <- space? "max_sessions" space? count:number space? ";"? space? `
  {max_sessions, pl(count,Node)}
`;

read_queue_option <- space? "read_queue" space? count:number space? ";"? space? `
  {read_queue, pl(count,Node)}
`;

transcoder_option <- space? "transcoder" space? settings:(transcoder_setting*) ";"? space? `
  {transcoder, pl(settings, Node)}
`;

transcoder_setting <- "vb=" video_bitrate:bitrate space? / "ab=" audio_bitrate:bitrate space? /
  "config=" config:string space? `
  lists:nth(2,Node)
`;


time <- num:number scale:("m" / "h" / "d") `
  Number = pl(num,Node),
  case pl(scale,Node) of
    <<"m">> -> Number*60;
    <<"h">> -> Number*3600;
    <<"d">> -> Number*24*3600
  end
`;


disk <- num:number scale:("K" / "M" / "G") `
  Number = pl(num,Node),
  case pl(scale,Node) of
    <<"K">> -> Number*1024;
    <<"M">> -> Number*1024*1024;
    <<"G">> -> Number*1024*1024*1024
  end
`;

bitrate <- num:number "k" `
  pl(num,Node)*1000
`;

string <- (nonspace)+ `iolist_to_binary(Node)`;

nonspace <- [^ ;\{\}\n\t\r] ~;




number <- [0-9]+ `list_to_integer(binary_to_list(iolist_to_binary(Node)))`;

percent <- num:number "%" `pl(num,Node)`;

space <- (white / comment_to_eol)+ `
  space
`;

white <- [ \t\n\r] ~;
comment_to_eol <- !'%{' ('#' / '%') (!"\n" .)* ~;



`
-record(opts, {
  prefix,
  domains = [],
  auth = true,
  dvr,
  dvr_limit,
  disk_limit,
  cache,
  urls = [],
  push = [],
  publish_enabled,
  udp,
  multicast_loop,
  thumb,
  retry_limit,
  password,
  max_sessions,
  read_queue,
  clients_timeout,
  transcoder
}).


apply_stream_options(Name, Options) ->
  #opts{} = Opts = apply_options(Options),
  {stream, Name, urls(Opts), make_options(Opts)}.

l(_, undefined) -> [];
l(Key, Value) -> [{Key,Value}].

make_options(#opts{dvr = DVR, dvr_limit = Time, disk_limit = Disk, domains = Domains, udp = UDP, multicast_loop = MC,
  thumb = Thumb, auth = Auth, cache = Cache, retry_limit = RetryLimit, clients_timeout = ClientsTimeout,
  password = Password, push = Push, publish_enabled = PublishEnabled, max_sessions = MaxSessions,
  read_queue = ReadQueue, prefix = Prefix, transcoder = Transcoder}) ->
  AuthOptions = 
  case Domains of
    [] -> [];
    _ -> [{domains,Domains}]
  end,

  case DVR of
    undefined -> [];
    _ ->
      [{dvr,DVR}] ++ case Time of
        undefined -> [];
        _ -> [{dvr_limit,Time}]
      end ++ case Disk of
        undefined -> [];
        _ -> [{disk_limit,Disk}]
      end
  end ++
  case UDP of
    undefined -> [];
    _ when MC == true -> [{udp,<<"udp://", UDP/binary>>},{multicast_loop,true}];
    _ -> [{udp,<<"udp://", UDP/binary>>}]
  end ++ l(thumb,Thumb) ++ l(transcoder,Transcoder) ++ case Cache of
    undefined -> [];
    {CachePath,CacheOpts} -> [{cache,CachePath,CacheOpts}]
  end ++ l(retry_limit,RetryLimit) ++ l(clients_timeout,ClientsTimeout) ++
  l(password,Password) ++ l(publish_enabled,PublishEnabled) ++ l(max_sessions,MaxSessions) ++
  case ReadQueue of
    undefined -> [];
    _ -> [{read_queue,ReadQueue,Prefix}]
  end ++
  [{push,P} || P <- Push]++
  case Auth of
    _ when AuthOptions =/= [] -> [{auth,Auth,AuthOptions}];
    true -> [];
    _ -> [{auth, Auth, []}]
  end.


apply_options(Options) -> apply_options(#opts{}, Options).

urls(#opts{urls = URLs}) -> URLs.



apply_options(#opts{urls = URLs} = O, [{url,URL}|Acc]) ->
  apply_options(O#opts{urls = URLs ++ [URL]}, Acc);

apply_options(#opts{} = O, [{urls,URLs2}|Acc]) ->
  apply_options(O#opts{urls = URLs2}, Acc);

apply_options(#opts{} = O, [{dvr,DVR, Time, Disk}|Acc]) ->
  apply_options(O#opts{dvr = DVR, dvr_limit = Time, disk_limit = Disk}, Acc);

apply_options(#opts{domains = D} = O, [{domain,Domain}|Acc]) ->
  apply_options(O#opts{domains = D ++ [Domain]}, Acc);

apply_options(#opts{} = O, [{domains,Domains}|Acc]) ->
  apply_options(O#opts{domains = Domains}, Acc);

apply_options(#opts{} = O, [{cache,Cache,Opts}|Acc]) ->
  apply_options(O#opts{cache = {Cache,Opts}}, Acc);

apply_options(#opts{} = O, [{udp,<<"udp://", UDP/binary>>}|Acc]) ->
  apply_options(O#opts{udp = UDP}, Acc);

apply_options(#opts{} = O, [{udp,UDP}|Acc]) ->
  apply_options(O#opts{udp = UDP}, Acc);

apply_options(#opts{} = O, [{udp,UDP,multicast_loop}|Acc]) ->
  apply_options(O#opts{udp = UDP,multicast_loop = true}, Acc);

apply_options(#opts{} = O, [{auth,URL}|Acc]) ->
  apply_options(O#opts{auth = URL}, Acc);

apply_options(#opts{} = O, [{thumb,true}|Acc]) ->
  apply_options(O#opts{thumb = true}, Acc);

apply_options(#opts{} = O, [{retry_limit,Limit}|Acc]) ->
  apply_options(O#opts{retry_limit = Limit}, Acc);

apply_options(#opts{} = O, [{clients_timeout,ClientsTimeout}|Acc]) ->
  apply_options(O#opts{clients_timeout = ClientsTimeout}, Acc);

apply_options(#opts{} = O, [{password,Password}|Acc]) ->
  apply_options(O#opts{password = Password}, Acc);

apply_options(#opts{push = Push} = O, [{push,P}|Acc]) ->
  apply_options(O#opts{push = Push ++ [P]}, Acc);

apply_options(#opts{} = O, [{publish_enabled,true}|Acc]) ->
  apply_options(O#opts{publish_enabled = true}, Acc);

apply_options(#opts{} = O, [{max_sessions,S}|Acc]) ->
  apply_options(O#opts{max_sessions = S}, Acc);

apply_options(#opts{} = O, [{read_queue,Q}|Acc]) ->
  apply_options(O#opts{read_queue = Q}, Acc);

apply_options(#opts{} = O, [{transcoder,TC}|Acc]) ->
  apply_options(O#opts{transcoder = TC}, Acc);

apply_options(#opts{} = O, []) -> O.

pl(Key, Proplist) -> proplists:get_value(Key, Proplist).
`