diff --git a/srt.go b/srt.go index 2cbb323..a47321a 100644 --- a/srt.go +++ b/srt.go @@ -38,11 +38,11 @@ func ReadFromSRT(i io.Reader) (o *Subtitles, err error) { o = NewSubtitles() var scanner = bufio.NewScanner(i) - styles := StyleAttributes{} // Scan var line string var lineNum int var s = &Item{} + var sa = &StyleAttributes{} for scanner.Scan() { // Fetch line line = strings.TrimSpace(scanner.Text()) @@ -59,9 +59,8 @@ func ReadFromSRT(i io.Reader) (o *Subtitles, err error) { // Line contains time boundaries if strings.Contains(line, srtTimeBoundariesSeparator) { - - // reset styles - styles = StyleAttributes{} + // Reset style attributes + sa = &StyleAttributes{} // Remove last item of previous subtitle since it should be the index. // If the last line is empty then the item is missing an index. @@ -123,8 +122,7 @@ func ReadFromSRT(i io.Reader) (o *Subtitles, err error) { o.Items = append(o.Items, s) } else { // Add text - var l Line - if l, styles = parseTextSrt(line, styles); len(l.Items) > 0 { + if l := parseTextSrt(line, sa); len(l.Items) > 0 { s.Lines = append(s.Lines, l) } } @@ -133,12 +131,11 @@ func ReadFromSRT(i io.Reader) (o *Subtitles, err error) { } // parseTextSrt parses the input line to fill the Line -func parseTextSrt(i string, styles StyleAttributes) (Line, StyleAttributes) { +func parseTextSrt(i string, sa *StyleAttributes) (o Line) { // special handling needed for empty line - o := Line{} - if i == "" { + if strings.TrimSpace(i) == "" { o.Items = []LineItem{{Text: ""}} - return o, styles + return } // Create tokenizer @@ -164,51 +161,51 @@ func parseTextSrt(i string, styles StyleAttributes) (Line, StyleAttributes) { // Parse italic/bold/underline switch token.Data { case "b": - styles.SRTBold = false + sa.SRTBold = false case "i": - styles.SRTItalics = false + sa.SRTItalics = false case "u": - styles.SRTUnderline = false + sa.SRTUnderline = false case "font": - styles.SRTColor = nil + sa.SRTColor = nil } case html.StartTagToken: // Parse italic/bold/underline switch token.Data { case "b": - styles.SRTBold = true + sa.SRTBold = true case "i": - styles.SRTItalics = true + sa.SRTItalics = true case "u": - styles.SRTUnderline = true + sa.SRTUnderline = true case "font": if c := htmlTokenAttribute(&token, "color"); c != nil { - styles.SRTColor = c + sa.SRTColor = c } } case html.TextToken: if s := strings.TrimSpace(raw); s != "" { // Get style attribute - var sa *StyleAttributes - if styles.SRTBold || styles.SRTColor != nil || styles.SRTItalics || styles.SRTUnderline { - sa = &StyleAttributes{ - SRTBold: styles.SRTBold, - SRTColor: styles.SRTColor, - SRTItalics: styles.SRTItalics, - SRTUnderline: styles.SRTUnderline, + var styleAttributes *StyleAttributes + if sa.SRTBold || sa.SRTColor != nil || sa.SRTItalics || sa.SRTUnderline { + styleAttributes = &StyleAttributes{ + SRTBold: sa.SRTBold, + SRTColor: sa.SRTColor, + SRTItalics: sa.SRTItalics, + SRTUnderline: sa.SRTUnderline, } - sa.propagateSRTAttributes() + styleAttributes.propagateSRTAttributes() } // Append item o.Items = append(o.Items, LineItem{ - InlineStyle: sa, + InlineStyle: styleAttributes, Text: unescapeHTML(s), }) } } } - return o, styles + return } // formatDurationSRT formats an .srt duration diff --git a/webvtt.go b/webvtt.go index 0b79dbf..af75a93 100644 --- a/webvtt.go +++ b/webvtt.go @@ -121,7 +121,6 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { var scanner = bufio.NewScanner(i) var line string var lineNum int - webVTTTagStack := make([]WebVTTTag, 0, 16) // Skip the header for scanner.Scan() { @@ -142,7 +141,7 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { var blockName string var comments []string var index int - var webVTTStyles *StyleAttributes + var sa = &StyleAttributes{} for scanner.Scan() { // Fetch line @@ -163,13 +162,14 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { // Reset block name, if we are not in the middle of CSS. // If we are in STYLE block and the CSS is empty or we meet the right brace at the end of last line, // then we are not in CSS and can switch to parse next WebVTT block. - if blockName != webvttBlockNameStyle || webVTTStyles == nil || - len(webVTTStyles.WebVTTStyles) == 0 || - strings.HasSuffix(webVTTStyles.WebVTTStyles[len(webVTTStyles.WebVTTStyles)-1], "}") { + if blockName != webvttBlockNameStyle || sa == nil || + len(sa.WebVTTStyles) == 0 || + strings.HasSuffix(sa.WebVTTStyles[len(sa.WebVTTStyles)-1], "}") { blockName = "" } - // Reset tag stack - webVTTTagStack = make([]WebVTTTag, 0, 16) + + // Reset WebVTTTags + sa.WebVTTTags = []WebVTTTag{} // Region case strings.HasPrefix(line, "Region: "): @@ -211,9 +211,9 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { blockName = webvttBlockNameStyle if _, ok := o.Styles[webvttDefaultStyleID]; !ok { - webVTTStyles = &StyleAttributes{} + sa = &StyleAttributes{} o.Styles[webvttDefaultStyleID] = &Style{ - InlineStyle: webVTTStyles, + InlineStyle: sa, ID: webvttDefaultStyleID, } } @@ -318,12 +318,11 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { case webvttBlockNameComment: comments = append(comments, line) case webvttBlockNameStyle: - webVTTStyles.WebVTTStyles = append(webVTTStyles.WebVTTStyles, line) + sa.WebVTTStyles = append(sa.WebVTTStyles, line) case webvttBlockNameText: // Parse line - if l, stack := parseTextWebVTT(line, webVTTTagStack); len(l.Items) > 0 { + if l := parseTextWebVTT(line, sa); len(l.Items) > 0 { item.Lines = append(item.Lines, l) - webVTTTagStack = stack } default: // This is the ID @@ -335,10 +334,9 @@ func ReadFromWebVTT(i io.Reader) (o *Subtitles, err error) { } // parseTextWebVTT parses the input line to fill the Line -func parseTextWebVTT(i string, webVTTTagStack []WebVTTTag) (Line, []WebVTTTag) { +func parseTextWebVTT(i string, sa *StyleAttributes) (o Line) { // Create tokenizer tr := html.NewTokenizer(strings.NewReader(i)) - o := Line{} // Loop for { @@ -352,8 +350,8 @@ func parseTextWebVTT(i string, webVTTTagStack []WebVTTTag) (Line, []WebVTTTag) { switch t { case html.EndTagToken: // Pop the top of stack if we meet end tag - if len(webVTTTagStack) > 0 { - webVTTTagStack = webVTTTagStack[:len(webVTTTagStack)-1] + if len(sa.WebVTTTags) > 0 { + sa.WebVTTTags = sa.WebVTTTags[:len(sa.WebVTTTags)-1] } case html.StartTagToken: if matches := webVTTRegexpTag.FindStringSubmatch(string(tr.Raw())); len(matches) > 4 { @@ -381,7 +379,7 @@ func parseTextWebVTT(i string, webVTTTagStack []WebVTTTag) (Line, []WebVTTTag) { } // Push the tag to stack - webVTTTagStack = append(webVTTTagStack, WebVTTTag{ + sa.WebVTTTags = append(sa.WebVTTTags, WebVTTTag{ Name: tagName, Classes: classes, Annotation: annotation, @@ -390,21 +388,21 @@ func parseTextWebVTT(i string, webVTTTagStack []WebVTTTag) (Line, []WebVTTTag) { case html.TextToken: // Get style attribute - var sa *StyleAttributes - if len(webVTTTagStack) > 0 { - tags := make([]WebVTTTag, len(webVTTTagStack)) - copy(tags, webVTTTagStack) - sa = &StyleAttributes{ + var styleAttributes *StyleAttributes + if len(sa.WebVTTTags) > 0 { + tags := make([]WebVTTTag, len(sa.WebVTTTags)) + copy(tags, sa.WebVTTTags) + styleAttributes = &StyleAttributes{ WebVTTTags: tags, } - sa.propagateWebVTTAttributes() + styleAttributes.propagateWebVTTAttributes() } // Append items - o.Items = append(o.Items, parseTextWebVTTTextToken(sa, string(tr.Raw()))...) + o.Items = append(o.Items, parseTextWebVTTTextToken(styleAttributes, string(tr.Raw()))...) } } - return o, webVTTTagStack + return } func parseTextWebVTTTextToken(sa *StyleAttributes, line string) (ret []LineItem) { diff --git a/webvtt_internal_test.go b/webvtt_internal_test.go index 6845ffd..a81f224 100644 --- a/webvtt_internal_test.go +++ b/webvtt_internal_test.go @@ -13,7 +13,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When both voice tags are available", func(t *testing.T) { testData := `Correct tag` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, "Bob", s.VoiceName) assert.Equal(t, 1, len(s.Items)) assert.Equal(t, "Correct tag", s.Items[0].Text) @@ -22,7 +22,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When there is no end tag", func(t *testing.T) { testData := ` Text without end tag` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, "Bob", s.VoiceName) assert.Equal(t, 1, len(s.Items)) assert.Equal(t, "Text without end tag", s.Items[0].Text) @@ -31,7 +31,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When the end tag is correct", func(t *testing.T) { testData := `Incorrect end tag` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, "Bob", s.VoiceName) assert.Equal(t, 1, len(s.Items)) assert.Equal(t, "Incorrect end tag", s.Items[0].Text) @@ -40,7 +40,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When inline timestamps are included", func(t *testing.T) { testData := `<00:01:01.000>With inline <00:01:02.000>timestamps` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, 2, len(s.Items)) assert.Equal(t, "With inline", s.Items[0].Text) assert.Equal(t, time.Minute+time.Second, s.Items[0].StartAt) @@ -51,7 +51,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When inline timestamps together", func(t *testing.T) { testData := `<00:01:01.000><00:01:02.000>With timestamp tags together` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, 1, len(s.Items)) assert.Equal(t, "With timestamp tags together", s.Items[0].Text) assert.Equal(t, time.Minute+2*time.Second, s.Items[0].StartAt) @@ -60,7 +60,7 @@ func TestParseTextWebVTT(t *testing.T) { t.Run("When inline timestamps is at end", func(t *testing.T) { testData := `With end timestamp<00:01:02.000>` - s, _ := parseTextWebVTT(testData, make([]WebVTTTag, 0, 16)) + s := parseTextWebVTT(testData, &StyleAttributes{}) assert.Equal(t, 1, len(s.Items)) assert.Equal(t, "With end timestamp", s.Items[0].Text) assert.Equal(t, time.Duration(0), s.Items[0].StartAt)