Current section

Files

Jump to
glubs src glubs@webvtt.erl
Raw

src/glubs@webvtt.erl

-module(glubs@webvtt).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([to_string/1, parse/1, tokenize/1]).
-export_type([item/0, web_vtt/0, token/0, tokenization_error/0]).
-type item() :: {note, binary()} |
{style, binary()} |
{cue,
gleam@option:option(binary()),
integer(),
integer(),
binary(),
list({binary(), binary()})}.
-type web_vtt() :: {web_vtt,
list({binary(), binary()}),
gleam@option:option(binary()),
list(item())}.
-type token() :: {start_tag,
binary(),
list(binary()),
gleam@option:option(binary())} |
{text, binary()} |
{timestamp, integer()} |
{end_tag, binary()}.
-type tokenization_error() :: invalid_start_token | invalid_end_token.
-spec header_to_string(gleam@option:option(binary())) -> gleam@string_builder:string_builder().
header_to_string(Comment) ->
case Comment of
{some, Comment@1} ->
gleam@string_builder:from_strings([<<"WEBVTT "/utf8>>, Comment@1]);
none ->
gleam@string_builder:from_string(<<"WEBVTT"/utf8>>)
end.
-spec metadata_to_string(list({binary(), binary()})) -> gleam@string_builder:string_builder().
metadata_to_string(Metadata) ->
case gleam@list:is_empty(Metadata) of
true ->
gleam@string_builder:new();
false ->
_pipe = Metadata,
_pipe@1 = gleam@list:map(
_pipe,
fun(Item) ->
Separator = case erlang:element(1, Item) =:= <<"X-TIMESTAMP-MAP"/utf8>> of
true ->
<<"="/utf8>>;
false ->
<<": "/utf8>>
end,
gleam@string_builder:from_strings(
[erlang:element(1, Item),
Separator,
erlang:element(2, Item)]
)
end
),
gleam@string_builder:join(_pipe@1, <<"\n"/utf8>>)
end.
-spec settings_to_string(list({binary(), binary()})) -> gleam@string_builder:string_builder().
settings_to_string(Settings) ->
case Settings of
[] ->
gleam@string_builder:new();
Settings@1 ->
_pipe = Settings@1,
_pipe@1 = gleam@list:map(
_pipe,
fun(Item) ->
gleam@string_builder:from_strings(
[erlang:element(1, Item),
<<":"/utf8>>,
erlang:element(2, Item)]
)
end
),
_pipe@2 = gleam@string_builder:join(_pipe@1, <<" "/utf8>>),
gleam@string_builder:prepend(_pipe@2, <<" "/utf8>>)
end.
-spec item_to_string(item()) -> gleam@string_builder:string_builder().
item_to_string(Item) ->
case Item of
{note, Content} ->
case gleam_stdlib:contains_string(Content, <<"\n"/utf8>>) of
true ->
gleam@string_builder:from_strings(
[<<"NOTE\n"/utf8>>, Content]
);
false ->
gleam@string_builder:from_strings(
[<<"NOTE "/utf8>>, Content]
)
end;
{style, Content@1} ->
gleam@string_builder:from_strings([<<"STYLE\n"/utf8>>, Content@1]);
{cue, Id, Start_time, End_time, Payload, Settings} ->
Start_time@1 = glubs@internal@timestamp:to_string(
Start_time,
<<"."/utf8>>
),
End_time@1 = glubs@internal@timestamp:to_string(
End_time,
<<"."/utf8>>
),
Settings_builder = settings_to_string(Settings),
Timestamp = begin
_pipe = Start_time@1,
_pipe@1 = gleam@string_builder:append(_pipe, <<" --> "/utf8>>),
_pipe@2 = gleam@string_builder:append_builder(
_pipe@1,
End_time@1
),
gleam@string_builder:append_builder(_pipe@2, Settings_builder)
end,
_pipe@5 = case Id of
{some, Id@1} ->
_pipe@3 = gleam@string_builder:from_string(Id@1),
_pipe@4 = gleam@string_builder:append(
_pipe@3,
<<"\n"/utf8>>
),
gleam@string_builder:append_builder(_pipe@4, Timestamp);
none ->
Timestamp
end,
_pipe@6 = gleam@string_builder:append(_pipe@5, <<"\n"/utf8>>),
gleam@string_builder:append(_pipe@6, Payload)
end.
-spec items_to_string(list(item())) -> gleam@string_builder:string_builder().
items_to_string(Items) ->
case gleam@list:is_empty(Items) of
true ->
gleam@string_builder:new();
false ->
_pipe = Items,
_pipe@1 = gleam@list:map(_pipe, fun item_to_string/1),
_pipe@2 = gleam@string_builder:join(_pipe@1, <<"\n\n"/utf8>>),
gleam@string_builder:prepend(_pipe@2, <<"\n"/utf8>>)
end.
-spec to_string(web_vtt()) -> binary().
to_string(Webvtt) ->
{web_vtt, Metadata, Comment, Items} = case Webvtt of
{web_vtt, _, _, _} -> Webvtt;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glubs/webvtt"/utf8>>,
function => <<"to_string"/utf8>>,
line => 49})
end,
_pipe = [header_to_string(Comment),
metadata_to_string(Metadata),
items_to_string(Items)],
_pipe@1 = gleam@list:filter(
_pipe,
fun(B) -> gleam@string_builder:is_empty(B) =:= false end
),
_pipe@2 = gleam@string_builder:join(_pipe@1, <<"\n"/utf8>>),
_pipe@3 = gleam@string_builder:append(_pipe@2, <<"\n"/utf8>>),
gleam@string_builder:to_string(_pipe@3).
-spec parse_metadata(list(binary())) -> {ok, list({binary(), binary()})} |
{error, binary()}.
parse_metadata(Metadata) ->
_pipe = Metadata,
gleam@list:try_map(_pipe, fun(Meta) -> case Meta of
<<"X-TIMESTAMP-MAP="/utf8, Header/binary>> ->
{ok, {<<"X-TIMESTAMP-MAP"/utf8>>, Header}};
_ ->
case gleam@string:split_once(Meta, <<": "/utf8>>) of
{ok, Entry} ->
{ok, Entry};
{error, nil} ->
{error, <<"Invalid metadata item"/utf8>>}
end
end end).
-spec parse_comment(binary()) -> {ok, gleam@option:option(binary())} |
{error, binary()}.
parse_comment(Header) ->
case Header of
<<"WEBVTT"/utf8>> ->
{ok, none};
<<"WEBVTT\t"/utf8, Comment/binary>> ->
{ok, {some, Comment}};
<<"WEBVTT "/utf8, Comment@1/binary>> ->
{ok, {some, Comment@1}};
<<"WEBVTT"/utf8, _/binary>> ->
{error, <<"Header comment must start with space or tab"/utf8>>};
_ ->
{error, <<"Must start with \"WEBVTT\""/utf8>>}
end.
-spec parse_note(binary()) -> {ok, item()} | {error, binary()}.
parse_note(Note) ->
case Note of
<<"NOTE\n"/utf8, Note@1/binary>> ->
{ok, {note, Note@1}};
<<"NOTE "/utf8, Note@2/binary>> ->
{ok, {note, Note@2}};
_ ->
{error, <<"Invalid note"/utf8>>}
end.
-spec parse_style(binary()) -> {ok, item()} | {error, binary()}.
parse_style(Style) ->
case Style of
<<"STYLE\n"/utf8, Style@1/binary>> ->
{ok, {style, Style@1}};
_ ->
{error, <<"Invalid style"/utf8>>}
end.
-spec parse_settings(binary()) -> {ok, list({binary(), binary()})} |
{error, binary()}.
parse_settings(Settings) ->
case Settings /= <<""/utf8>> of
true ->
_pipe = Settings,
_pipe@1 = gleam@string:split(_pipe, <<" "/utf8>>),
gleam@list:try_map(
_pipe@1,
fun(Setting) ->
case gleam@string:split_once(Setting, <<":"/utf8>>) of
{ok, Item} ->
{ok, Item};
{error, nil} ->
{error, <<"Invalid cue settings"/utf8>>}
end
end
);
false ->
{ok, []}
end.
-spec parse_cue_id(binary()) -> {ok, {gleam@option:option(binary()), binary()}} |
{error, binary()}.
parse_cue_id(Cue) ->
case gleam@string:split_once(Cue, <<"\n"/utf8>>) of
{ok, {Id, Rest}} ->
case gleam_stdlib:contains_string(Id, <<"-->"/utf8>>) of
true ->
{ok, {none, Cue}};
false ->
{ok, {{some, Id}, Rest}}
end;
{error, nil} ->
{error, <<"Invalid cue"/utf8>>}
end.
-spec parse_cue(binary()) -> {ok, item()} | {error, binary()}.
parse_cue(Cue) ->
gleam@result:'try'(
parse_cue_id(Cue),
fun(_use0) ->
{Id, Rest} = _use0,
case gleam@string:split_once(Rest, <<"\n"/utf8>>) of
{ok, {Line, Payload}} ->
gleam@result:'try'(
glubs@internal@timestamp:parse_range(Line, <<"."/utf8>>),
fun(_use0@1) ->
{Start, End, Rest@1} = _use0@1,
gleam@result:'try'(
parse_settings(Rest@1),
fun(Settings) ->
{ok,
{cue, Id, Start, End, Payload, Settings}}
end
)
end
);
{error, nil} ->
{error, <<"Invalid cue"/utf8>>}
end
end
).
-spec parse_item(binary()) -> {ok, item()} | {error, binary()}.
parse_item(Item) ->
_pipe = Item,
_pipe@1 = parse_note(_pipe),
_pipe@2 = gleam@result:try_recover(_pipe@1, fun(_) -> parse_style(Item) end),
gleam@result:try_recover(_pipe@2, fun(_) -> parse_cue(Item) end).
-spec parse(binary()) -> {ok, web_vtt()} | {error, binary()}.
parse(Webvtt) ->
_assert_subject = begin
_pipe = Webvtt,
_pipe@1 = gleam@string:replace(_pipe, <<"\r\n"/utf8>>, <<"\n"/utf8>>),
_pipe@2 = gleam@string:trim_right(_pipe@1),
gleam@string:split(_pipe@2, <<"\n\n"/utf8>>)
end,
[Header | Body] = case _assert_subject of
[_ | _] -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glubs/webvtt"/utf8>>,
function => <<"parse"/utf8>>,
line => 32})
end,
_assert_subject@1 = gleam@string:split(Header, <<"\n"/utf8>>),
[Header@1 | Metadata] = case _assert_subject@1 of
[_ | _] -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"glubs/webvtt"/utf8>>,
function => <<"parse"/utf8>>,
line => 38})
end,
gleam@result:'try'(
parse_comment(Header@1),
fun(Comment) ->
gleam@result:'try'(
parse_metadata(Metadata),
fun(Metadata@1) ->
gleam@result:'try'(
gleam@list:try_map(Body, fun parse_item/1),
fun(Items) ->
{ok, {web_vtt, Metadata@1, Comment, Items}}
end
)
end
)
end
).
-spec parse_tag_and_classes(binary()) -> {binary(), list(binary())}.
parse_tag_and_classes(Input) ->
_assert_subject = gleam@string:split(Input, <<"."/utf8>>),
[Tag | Classes] = case _assert_subject of
[_ | _] -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glubs/webvtt"/utf8>>,
function => <<"parse_tag_and_classes"/utf8>>,
line => 320})
end,
{Tag, Classes}.
-spec parse_start_tag(binary()) -> token().
parse_start_tag(Input) ->
case gleam@string:split_once(Input, <<" "/utf8>>) of
{ok, {Tag_and_classes, Annotation}} ->
{Tag, Classes} = parse_tag_and_classes(Tag_and_classes),
{start_tag, Tag, Classes, {some, Annotation}};
{error, _} ->
{Tag@1, Classes@1} = parse_tag_and_classes(Input),
{start_tag, Tag@1, Classes@1, none}
end.
-spec do_tokenize(binary(), list(token())) -> {ok, list(token())} |
{error, tokenization_error()}.
do_tokenize(Payload, Acc) ->
case Payload of
<<""/utf8>> ->
{ok, Acc};
<<"</"/utf8, Rest/binary>> ->
case gleam@string:split_once(Rest, <<">"/utf8>>) of
{ok, {Tag, Rest@1}} ->
do_tokenize(Rest@1, [{end_tag, Tag} | Acc]);
{error, nil} ->
{error, invalid_end_token}
end;
<<"<"/utf8, Rest@2/binary>> ->
case gleam@string:split_once(Rest@2, <<">"/utf8>>) of
{ok, {Tag@1, Rest@3}} ->
case glubs@internal@timestamp:parse(Tag@1, <<"."/utf8>>) of
{ok, Ts} ->
do_tokenize(Rest@3, [{timestamp, Ts} | Acc]);
{error, _} ->
do_tokenize(Rest@3, [parse_start_tag(Tag@1) | Acc])
end;
{error, nil} ->
{error, invalid_start_token}
end;
Text ->
case gleam@string:split_once(Text, <<"<"/utf8>>) of
{ok, {Content, Rest@4}} ->
do_tokenize(
<<"<"/utf8, Rest@4/binary>>,
[{text, Content} | Acc]
);
{error, nil} ->
{ok, [{text, Text} | Acc]}
end
end.
-spec tokenize(binary()) -> {ok, list(token())} | {error, tokenization_error()}.
tokenize(Payload) ->
_pipe = Payload,
_pipe@1 = do_tokenize(_pipe, []),
gleam@result:map(_pipe@1, fun gleam@list:reverse/1).