commit 1fbee67ca51c80c4a1a167e577983480f0efdb03
parent 12b4cf5015a6017ee960c93072ddffdc51bc746c
Author: Oliver Lowe <o@olowe.co>
Date: Sat, 1 Jun 2024 19:25:17 +1000
m3u8: separate printing and validation when writing playlist renditions
This will make it way easier to test how we turn one of our Rendition structs into text.
References: https://github.com/untangledco/streaming/issues/7
Diffstat:
2 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/m3u8/master.go b/m3u8/master.go
@@ -22,6 +22,43 @@ type Rendition struct {
Channels []string
}
+func (r Rendition) String() string {
+ var attrs []string
+ attrs = append(attrs, fmt.Sprintf("NAME=%q", r.Name))
+ attrs = append(attrs, fmt.Sprintf("TYPE=%s", r.Type))
+ if r.URI != "" {
+ attrs = append(attrs, fmt.Sprintf("URI=%q", r.URI))
+ }
+ attrs = append(attrs, fmt.Sprintf("GROUP-ID=%q", r.Group))
+ if r.Language != "" {
+ attrs = append(attrs, fmt.Sprintf("LANGUAGE=%q", r.Language))
+ }
+ if r.AssocLanguage != "" {
+ attrs = append(attrs, fmt.Sprintf("ASSOC-LANGUAGE=%q", r.AssocLanguage))
+ }
+ if r.Default {
+ attrs = append(attrs, "DEFAULT=YES")
+ }
+ if r.AutoSelect {
+ attrs = append(attrs, "AUTOSELECT=YES")
+ }
+ if r.Forced {
+ attrs = append(attrs, "FORCED=YES")
+ }
+ if r.Type == MediaClosedCaptions && r.InstreamID != nil {
+ attrs = append(attrs, fmt.Sprintf("INSTREAM-ID=%q", r.InstreamID))
+ }
+ if len(r.Characteristics) > 0 {
+ chars := strings.Join(r.Characteristics, ",")
+ attrs = append(attrs, fmt.Sprintf("CHARACTERISTICS=%q", chars))
+ }
+ if len(r.Channels) > 0 {
+ channels := strings.Join(r.Channels, "/")
+ attrs = append(attrs, fmt.Sprintf("CHANNELS=%q", channels))
+ }
+ return tagRendition + ":" + strings.Join(attrs, ",")
+}
+
type MediaType uint8
const (
diff --git a/m3u8/write.go b/m3u8/write.go
@@ -34,54 +34,20 @@ func Encode(w io.Writer, p *Playlist) error {
if r.Name == "" {
return fmt.Errorf("empty name")
}
- fmt.Fprintf(w, tagRendition+":")
- // TODO(otl): use string slice, then strings.Join(s, ",")
- // then we don't worry about errors from w.Write
- fmt.Fprintf(w, "NAME=%q,", r.Name)
rname := fmt.Sprintf("rendition %s", r.Name)
if r.Type > MediaClosedCaptions {
return fmt.Errorf("%s: unknown type %s", rname, r.Type)
}
- fmt.Fprintf(w, "TYPE=%s,", r.Type)
- if r.URI != "" {
- fmt.Fprintf(w, "URI=%q,", r.URI)
- }
if r.Group == "" {
return fmt.Errorf("%s: empty group", rname)
}
- fmt.Fprintf(w, "GROUP-ID=%q,", r.Group)
- if r.Language != "" {
- fmt.Fprintf(w, "LANGUAGE=%q,", r.Language)
- }
- if r.AssocLanguage != "" {
- fmt.Fprintf(w, "ASSOC-LANGUAGE=%q,", r.AssocLanguage)
- }
- if r.Default {
- fmt.Fprint(w, "DEFAULT=YES,")
- }
- if r.AutoSelect {
- fmt.Fprint(w, "AUTOSELECT=YES,")
- }
- if r.Forced {
- fmt.Fprint(w, "FORCED=YES,")
- }
-
if r.Type != MediaClosedCaptions && r.InstreamID != nil {
return fmt.Errorf("%s: instream-id set but type is %s", rname, r.Type)
}
- if r.Type == MediaClosedCaptions {
- if r.InstreamID == nil {
- return fmt.Errorf("%s: nil instream-id", rname)
- }
- fmt.Fprintf(w, "INSTREAM-ID=%q,", r.InstreamID)
- }
- if len(r.Characteristics) > 0 {
- fmt.Fprintf(w, "CHARACTERISTICS=%q,", strings.Join(r.Characteristics, ","))
- }
- if len(r.Channels) > 0 {
- fmt.Fprintf(w, "CHANNELS=%q,", strings.Join(r.Channels, "/"))
+ if r.Type == MediaClosedCaptions && r.InstreamID == nil {
+ return fmt.Errorf("%s: nil instream-id", rname)
}
- fmt.Fprintln(w)
+ fmt.Fprintln(w, r)
}
for i, v := range p.Variants {