Commit Diff


commit - 3df4029f13d7c363d034d1c5aed186ba947ab8a6
commit + 62d865db8655e6d23b4fccc3280d73c3cdda8ce9
blob - a71b7f91295d88116f9117e9fd2185a9749e5751
blob + eb9edd1b313d85e154de73ebe1751394c2b0cdc8
--- m3u8/parse.go
+++ m3u8/parse.go
@@ -4,6 +4,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -32,70 +33,84 @@ func Decode(rd io.Reader) (*Playlist, error) {
 	if it.typ != itemTag || it.val != tagHead {
 		return nil, fmt.Errorf("expected head tag, got %q", it.val)
 	}
+
 	p := &Playlist{}
 	var err error
 	for it := range lex.items {
 		switch it.typ {
 		case itemError:
 			return p, errors.New(it.val)
-		case itemTag:
-			switch it.val {
-			case tagVersion:
-				it = <-lex.items
-				if p.Version != 0 {
-					return p, fmt.Errorf("parse %s: playlist version already specified", it)
-				}
-				p.Version, err = strconv.Atoi(it.val)
-				if err != nil {
-					return p, fmt.Errorf("parse playlist version: %w", err)
-				}
-			case tagIndependentSegments:
-				p.IndependentSegments = true
-			case tagVariant:
-				variant, err := parseVariant(lex.items)
-				if err != nil {
-					return p, fmt.Errorf("parse variant: %w", err)
-				}
-				p.Variants = append(p.Variants, *variant)
-			case tagRendition:
-				rend, err := parseRendition(lex.items)
-				if err != nil {
-					return p, fmt.Errorf("parse rendition: %w", err)
-				}
-				p.Media = append(p.Media, *rend)
-			case tagPlaylistType:
-				it = <-lex.items
-				typ, err := parsePlaylistType(it)
-				if err != nil {
-					return p, fmt.Errorf("parse playlist type: %w", err)
-				}
-				p.Type = typ
+		case itemNewline:
+			continue
+		default:
+			if it.typ != itemTag {
+				return p, fmt.Errorf("unexpected %s %q, expected tag", it.typ, it.val)
+			}
+		}
 
-			case tagTargetDuration:
-				it = <-lex.items
-				dur, err := parseTargetDuration(it)
-				if err != nil {
-					return p, fmt.Errorf("parse target duration: %w", err)
-				}
-				p.TargetDuration = dur
+		switch it.val {
+		case tagVersion:
+			it = <-lex.items
+			if p.Version != 0 {
+				return p, fmt.Errorf("parse %s: playlist version already specified", it)
+			}
+			p.Version, err = strconv.Atoi(it.val)
+			if err != nil {
+				return p, fmt.Errorf("parse playlist version: %w", err)
+			}
+		case tagIndependentSegments:
+			p.IndependentSegments = true
+		case tagVariant:
+			variant, err := parseVariant(lex.items)
+			if err != nil {
+				return p, fmt.Errorf("parse variant: %w", err)
+			}
+			p.Variants = append(p.Variants, *variant)
+		case tagRendition:
+			rend, err := parseRendition(lex.items)
+			if err != nil {
+				return p, fmt.Errorf("parse rendition: %w", err)
+			}
+			p.Media = append(p.Media, *rend)
+		case tagPlaylistType:
+			it = <-lex.items
+			typ, err := parsePlaylistType(it)
+			if err != nil {
+				return p, fmt.Errorf("parse playlist type: %w", err)
+			}
+			p.Type = typ
 
-			case tagSegmentDuration, tagByteRange, tagKey:
-				segment, err := parseSegment(lex.items, it)
-				if err != nil {
-					return p, fmt.Errorf("parse segment: %w", err)
-				}
-				p.Segments = append(p.Segments, *segment)
+		case tagTargetDuration:
+			it = <-lex.items
+			dur, err := parseTargetDuration(it)
+			if err != nil {
+				return p, fmt.Errorf("parse target duration: %w", err)
+			}
+			p.TargetDuration = dur
 
-			case tagEndList:
-				p.End = true
-			case tagMediaSequence:
-				it = <-lex.items
-				seq, err := strconv.Atoi(it.val)
-				if err != nil {
-					return p, fmt.Errorf("parse media sequence: %w", err)
-				}
-				p.Sequence = seq
+		case tagSegmentDuration, tagByteRange, tagKey:
+			segment, err := parseSegment(lex.items, it)
+			if err != nil {
+				return p, fmt.Errorf("parse segment: %w", err)
 			}
+			p.Segments = append(p.Segments, *segment)
+
+		case tagEndList:
+			p.End = true
+		case tagMediaSequence:
+			it = <-lex.items
+			seq, err := strconv.Atoi(it.val)
+			if err != nil {
+				return p, fmt.Errorf("parse media sequence: %w", err)
+			}
+			p.Sequence = seq
+		default:
+			if lex.debug {
+				fmt.Fprintln(os.Stderr, "unknown tag", it)
+			}
+			// throw away whatever is next; we don't support it but also don't want to
+			// return errors while this package is in development.
+			<-lex.items
 		}
 	}
 	return p, nil
blob - 0b9fdc9478e2542627b7c7f74521a3c2a8189985
blob + 0a40a92ff5f85cbd5b98a03af439c521889bd4ee
--- m3u8/segment.go
+++ m3u8/segment.go
@@ -147,48 +147,54 @@ func parseKey(items chan item) (Key, error) {
 			return key, errors.New(it.val)
 		case itemNewline:
 			return key, nil
-		case itemAttrName:
-			v := <-items
-			if v.typ != itemEquals {
-				return key, fmt.Errorf("expected %q after %s, got %s", "=", it.typ, v)
+		case itemComma:
+			continue
+		default:
+			if it.typ != itemAttrName {
+				return Key{}, fmt.Errorf("expected attribute name, got %s", it.val)
 			}
-			switch it.val {
-			case "METHOD":
-				v = <-items
-				key.Method = parseEncryptMethod(v.val)
-				if key.Method == encryptMethodInvalid {
-					return key, fmt.Errorf("bad encrypt method %q", v.val)
-				}
-			case "URI":
-				v = <-items
-				key.URI = strings.Trim(v.val, `"`)
-			case "IV":
-				v = <-items
-				b, err := hex.DecodeString(strings.TrimPrefix(v.val, "0x"))
+		}
+		v := <-items
+		if v.typ != itemEquals {
+			return key, fmt.Errorf("expected %q after %s, got %s", "=", it.typ, v)
+		}
+
+		switch it.val {
+		case "METHOD":
+			v = <-items
+			key.Method = parseEncryptMethod(v.val)
+			if key.Method == encryptMethodInvalid {
+				return key, fmt.Errorf("bad encrypt method %q", v.val)
+			}
+		case "URI":
+			v = <-items
+			key.URI = strings.Trim(v.val, `"`)
+		case "IV":
+			v = <-items
+			b, err := hex.DecodeString(strings.TrimPrefix(v.val, "0x"))
+			if err != nil {
+				return key, fmt.Errorf("parse initialisation vector: %w", err)
+			}
+			if len(b) != len(key.IV) {
+				return key, fmt.Errorf("bad initialisation length %d, want %d", len(b), len(key.IV))
+			}
+			copy(key.IV[:], b)
+		case "KEYFORMAT":
+			v = <-items
+			key.Format = strings.Trim(v.val, `"`)
+		case "KEYFORMATVERSIONS":
+			v = <-items
+			ss := strings.Split(v.val, "/")
+			key.FormatVersions = make([]uint32, len(ss))
+			for i := range ss {
+				n, err := strconv.Atoi(ss[i])
 				if err != nil {
-					return key, fmt.Errorf("parse initialisation vector: %w", err)
+					return key, fmt.Errorf("parse key format version: %w", err)
 				}
-				if len(b) != len(key.IV) {
-					return key, fmt.Errorf("bad initialisation length %d, want %d", len(b), len(key.IV))
-				}
-				copy(key.IV[:], b)
-			case "KEYFORMAT":
-				v = <-items
-				key.Format = strings.Trim(v.val, `"`)
-			case "KEYFORMATVERSIONS":
-				v = <-items
-				ss := strings.Split(v.val, "/")
-				key.FormatVersions = make([]uint32, len(ss))
-				for i := range ss {
-					n, err := strconv.Atoi(ss[i])
-					if err != nil {
-						return key, fmt.Errorf("parse key format version: %w", err)
-					}
-					key.FormatVersions[i] = uint32(n)
-				}
-			default:
-				return key, fmt.Errorf("unexpected attribute %q", it.val)
+				key.FormatVersions[i] = uint32(n)
 			}
+		default:
+			return key, fmt.Errorf("unexpected attribute %q", it.val)
 		}
 	}
 	return key, fmt.Errorf("unexpected end of tag")