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 }