github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/video/subtitle/srt/subrip.go (about)

     1  // Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package srt
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // Subtitle represents a subtitle.
    17  type Subtitle struct {
    18  	Number    int
    19  	Position  [4]int // X1,X2,Y1,Y2
    20  	StartTime time.Duration
    21  	EndTime   time.Duration
    22  	Texts     []string
    23  }
    24  
    25  func ParseFile(name string) (subtitles []Subtitle, err error) {
    26  	data, err := ioutil.ReadFile(name)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	return ParseData(data)
    31  }
    32  
    33  func ParseData(data []byte) (subtitles []Subtitle, err error) {
    34  	r := newLineReader(string(data))
    35  	for {
    36  		var sub Subtitle
    37  		if sub, err = readSubtitle(r); err != nil {
    38  			if err == io.EOF {
    39  				err = nil
    40  			}
    41  			return
    42  		}
    43  		subtitles = append(subtitles, sub)
    44  	}
    45  }
    46  
    47  func readSubtitle(r *lineReader) (sub Subtitle, err error) {
    48  	r.SkipBlankLine()
    49  	if err = sub.readNumber(r); err != nil {
    50  		return
    51  	}
    52  	if err = sub.readTimeAndPosition(r); err != nil {
    53  		return
    54  	}
    55  	if err = sub.readTexts(r); err != nil {
    56  		return
    57  	}
    58  	return
    59  }
    60  
    61  func (p *Subtitle) readNumber(r *lineReader) (err error) {
    62  	var s string
    63  	if s, err = r.ReadLine(); err != nil {
    64  		return err
    65  	}
    66  	if p.Number, err = strconv.Atoi(s); err != nil {
    67  		return fmt.Errorf("srt: bad number, line = %d", r.CurrentPos())
    68  	}
    69  	return nil
    70  }
    71  
    72  // 00:00:15,000 --> 00:00:18,000
    73  // 00:00:10,500 --> 00:00:13,000 X1:63 X2:223 Y1:43 Y2:58
    74  func (p *Subtitle) readTimeAndPosition(r *lineReader) (err error) {
    75  	var s string
    76  	if s, err = r.ReadLine(); err != nil {
    77  		return err
    78  	}
    79  	if err = p.readTimeValue(s, r.CurrentPos()); err != nil {
    80  		return err
    81  	}
    82  	if err = p.readPositionValue(s, r.CurrentPos()); err != nil {
    83  		return err
    84  	}
    85  	return nil
    86  }
    87  
    88  // 00:00:15,000 --> 00:00:18,000
    89  func (p *Subtitle) readTimeValue(s string, pos int) (err error) {
    90  	if len(s) < len(`00:00:15,000 --> 00:00:18,000`) {
    91  		return fmt.Errorf("srt: bad time, line = %d", pos)
    92  	}
    93  	p.StartTime, err = parseDuration(s[:12])
    94  	p.EndTime, err = parseDuration(s[17:])
    95  	if err != nil {
    96  		return fmt.Errorf("srt: bad time, line = %d", pos)
    97  	}
    98  	return nil
    99  }
   100  
   101  // 00:00:10,500 --> 00:00:13,000 X1:63 X2:223 Y1:43 Y2:58
   102  func (p *Subtitle) readPositionValue(s string, pos int) (err error) {
   103  	tags := [4]string{"X1", "X2", "Y1", "Y2"} // Need math p.Position's order
   104  	for i := 0; i < len(tags); i++ {
   105  		idx := strings.Index(s, tags[i])
   106  		if idx < 0 {
   107  			continue
   108  		}
   109  		t := s[idx+3:] // X1:??<spece><\t>...
   110  		if idx = strings.IndexAny(t, " \n\t\r\n"); idx >= 0 {
   111  			t = t[:idx]
   112  		}
   113  		if p.Position[i], err = strconv.Atoi(t); err != nil {
   114  			return fmt.Errorf("srt: bad position, line = %d", pos)
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  func (p *Subtitle) readTexts(r *lineReader) (err error) {
   121  	var s string
   122  	for {
   123  		if s, err = r.ReadLine(); err != nil {
   124  			return err
   125  		}
   126  		if strings.TrimSpace(s) == "" {
   127  			return nil
   128  		}
   129  		p.Texts = append(p.Texts, s)
   130  	}
   131  }