commit a5e36fbfb60aadd58d7a06c29272251c0a8f8c0e
parent 093b7480bc7275b0047361f2f87d3992bc71e5ee
Author: Oliver Lowe <o@olowe.co>
Date: Tue, 20 May 2025 15:51:19 +1000
m3u8: define segment parsing once
It's a bit of a hack that we read one item too many from the lexer.
This change modifies segment parsing by sending the already consumed
lexer items and subsequent lexer items back through another channel.
Maintaining the original order through a channel means we can parse a
segment in a single loop. Much shorter than dealing with the first
item and all other items separately.
This puts off looking into any redesigns of the lexer. For now...?
Diffstat:
| M | m3u8/segment.go | | | 58 | +++++++++++++++++++++------------------------------------- |
1 file changed, 21 insertions(+), 37 deletions(-)
diff --git a/m3u8/segment.go b/m3u8/segment.go
@@ -28,59 +28,43 @@ const (
// parseSegment returns the next segment from items and the leading
// item which indicated the start of a segment.
func parseSegment(items chan item, leading item) (*Segment, error) {
- var seg Segment
- switch leading.typ {
- case itemTag:
- switch leading.val {
- case tagSegmentDuration:
- it := <-items
- dur, err := parseSegmentDuration(it)
- if err != nil {
- return nil, fmt.Errorf("parse segment duration: %w", err)
- }
- seg.Duration = dur
- case tagKey:
- key, err := parseKey(items)
- if err != nil {
- return nil, fmt.Errorf("parse key: %w", err)
- }
- seg.Key = &key
- case tagMap:
- m, err := parseMap(items)
- if err != nil {
- return nil, fmt.Errorf("parse map: %w", err)
+ // we've already read one item, so send everything through again
+ // starting with the leading item to maintain the lexer's original order.
+ segItems := make(chan item)
+ go func() {
+ segItems <- leading
+ for it := range items {
+ if it.typ == itemURL {
+ segItems <- it
+ close(segItems)
+ return
}
- seg.Map = &m
- default:
- return nil, fmt.Errorf("parse leading item %s: unsupported", leading)
+ segItems <- it
}
- }
+ }()
- for it := range items {
+ var seg Segment
+ for it := range segItems {
switch it.typ {
+ case itemNewline:
+ continue
case itemError:
return nil, errors.New(it.val)
case itemURL:
seg.URI = it.val
return &seg, nil
- case itemNewline:
- continue
- default:
- if it.typ != itemTag {
- return nil, fmt.Errorf("unexpected %s", it)
- }
}
switch it.val {
case tagSegmentDuration:
- it = <-items
+ it = <-segItems
dur, err := parseSegmentDuration(it)
if err != nil {
return nil, fmt.Errorf("parse segment duration: %w", err)
}
seg.Duration = dur
case tagByteRange:
- it = <-items
+ it = <-segItems
r, err := parseByteRange(it.val)
if err != nil {
return nil, fmt.Errorf("parse byte range: %w", err)
@@ -89,19 +73,19 @@ func parseSegment(items chan item, leading item) (*Segment, error) {
case tagDiscontinuity:
seg.Discontinuity = true
case tagKey:
- key, err := parseKey(items)
+ key, err := parseKey(segItems)
if err != nil {
return nil, fmt.Errorf("parse key: %w", err)
}
seg.Key = &key
case tagMap:
- m, err := parseMap(items)
+ m, err := parseMap(segItems)
if err != nil {
return nil, fmt.Errorf("parse map: %w", err)
}
seg.Map = &m
case tagDateTime:
- it = <-items
+ it = <-segItems
t, err := time.Parse(time.RFC3339Nano, it.val)
if err != nil {
return nil, fmt.Errorf("bad date time tag: %w", err)