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]).
-export([to_string/1, parse/1, tokenize/1]).
-export_type([item/0, web_vtt/0, token/0, tokenization_error/0]).
-type item() :: {note, binary()} |
{cue, gleam@option:option(binary()), integer(), integer(), binary()}.
-type web_vtt() :: {web_vtt, 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([<<" "/utf8>>, Comment@1]);
none ->
gleam@string_builder:new()
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;
{cue, Id, Start_time, End_time, Payload} ->
Start_time@1 = glubs@timestamp:to_string(Start_time, <<"."/utf8>>),
End_time@1 = glubs@timestamp:to_string(End_time, <<"."/utf8>>),
Timestamp = begin
_pipe = Start_time@1,
_pipe@1 = gleam@string_builder:append(_pipe, <<" --> "/utf8>>),
gleam@string_builder:append_builder(_pipe@1, End_time@1)
end,
_pipe@4 = case Id of
{some, Id@1} ->
_pipe@2 = gleam@string_builder:from_string(Id@1),
_pipe@3 = gleam@string_builder:append(
_pipe@2,
<<"\n"/utf8>>
),
gleam@string_builder:append_builder(_pipe@3, Timestamp);
none ->
Timestamp
end,
_pipe@5 = gleam@string_builder:append(_pipe@4, <<"\n"/utf8>>),
gleam@string_builder:append(_pipe@5, Payload)
end.
-spec items_to_string(list(item())) -> gleam@string_builder:string_builder().
items_to_string(Items) ->
_pipe = Items,
_pipe@1 = gleam@list:map(_pipe, fun item_to_string/1),
gleam@string_builder:join(_pipe@1, <<"\n\n"/utf8>>).
-spec to_string(web_vtt()) -> binary().
to_string(Webvtt) ->
{web_vtt, 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 => 38})
end,
_pipe = <<"WEBVTT"/utf8>>,
_pipe@1 = gleam@string_builder:from_string(_pipe),
_pipe@2 = gleam@string_builder:append_builder(
_pipe@1,
header_to_string(Comment)
),
_pipe@3 = gleam@string_builder:append(_pipe@2, <<"\n\n"/utf8>>),
_pipe@4 = gleam@string_builder:append_builder(
_pipe@3,
items_to_string(Items)
),
_pipe@5 = gleam@string_builder:append(_pipe@4, <<"\n"/utf8>>),
gleam@string_builder:to_string(_pipe@5).
-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_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@timestamp:parse_range(Line, <<"."/utf8>>),
fun(_use0@1) ->
{Start, End} = _use0@1,
{ok, {cue, Id, Start, End, Payload}}
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),
gleam@result:try_recover(_pipe@1, fun(_) -> parse_cue(Item) end).
-spec parse(binary()) -> {ok, web_vtt()} | {error, binary()}.
parse(Webvtt) ->
[Header | Body] = 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@1 | _] = gleam@string:split(Header, <<"\n"/utf8>>),
gleam@result:'try'(
parse_comment(Header@1),
fun(Comment) ->
gleam@result:'try'(
gleam@list:try_map(Body, fun parse_item/1),
fun(Items) -> {ok, {web_vtt, Comment, Items}} end
)
end
).
-spec parse_tag_and_classes(binary()) -> {binary(), list(binary())}.
parse_tag_and_classes(Input) ->
[Tag | Classes] = gleam@string:split(Input, <<"."/utf8>>),
{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@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).