forked from martinlindhe/subtitles
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsrt.go
137 lines (122 loc) · 2.89 KB
/
srt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package subtitles
import (
"fmt"
"regexp"
"runtime"
"strconv"
"strings"
"time"
"unicode"
)
// Eol is the end of line characters to use when writing .srt data
var eol = "\n"
func init() {
if runtime.GOOS == "windows" {
eol = "\r\n"
}
}
func looksLikeSRT(s string) bool {
caps, err := NewFromSRT(s)
if err == nil && len(caps.Captions) >= 1 {
return true
}
return false
}
// NewFromSRT parses a .srt text into Subtitle, assumes s is a clean utf8 string
func NewFromSRT(s string) (res Subtitle, errs []error) {
var err error
re := regexp.MustCompile(`([0-9]+:*[0-9]+:[0-9]+[\.,]+[0-9]+)\s+-->\s+([0-9]+:*[0-9]+:[0-9]+[\.,]+[0-9]+)`)
lines := strings.Split(s, "\n")
outSeq := 1
for i, line := range lines {
matches := re.FindStringSubmatch(stripSpaces(line))
if len(matches) < 3 {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
_, err := strconv.Atoi(line)
if err == nil {
// if the next line is timecode
if i+1 < len(lines) &&
len(re.FindStringSubmatch(stripSpaces(lines[i+1]))) >= 3 {
// then skip this seq number
continue
}
}
// not time codes, so it may be text
ll := len(res.Captions) - 1
if ll >= 0 {
res.Captions[ll].Text = append(res.Captions[ll].Text, line)
}
continue
}
var o Caption
o.Seq = outSeq
o.Start, err = parseTime(matches[1])
if err != nil {
err = fmt.Errorf("srt: start error at line %d: %v", i, err)
errs = append(errs, err)
break
}
o.End, err = parseTime(matches[2])
if err != nil {
err = fmt.Errorf("srt: end error at line %d: %v", i, err)
errs = append(errs, err)
break
}
if removeLastEmptyCaption(&res, &o) {
outSeq--
} else {
res.Captions = append(res.Captions, o)
outSeq++
}
}
removeLastEmptyCaption(&res, nil)
return
}
func stripSpaces(line string) (r string) {
return strings.Map(func(r rune) rune {
if unicode.In(r, unicode.Number, unicode.Symbol,
unicode.Punct,
unicode.Dash, unicode.White_Space) {
return r
}
return -1
}, line)
}
func removeLastEmptyCaption(res *Subtitle, o *Caption) (removed bool) {
ll := len(res.Captions)
if ll > 0 && len(res.Captions[ll-1].Text) == 0 {
// remove the last caption if it was empty
if o != nil {
o.Seq--
res.Captions[ll-1] = *o
} else {
res.Captions = res.Captions[:ll-1]
}
return true
}
return false
}
// AsSRT renders the sub in .srt format
func (subtitle *Subtitle) AsSRT() (res string) {
for _, sub := range subtitle.Captions {
res += sub.AsSRT()
}
return
}
// AsSRT renders the caption as srt
func (cap Caption) AsSRT() string {
res := fmt.Sprintf("%d", cap.Seq) + eol +
TimeSRT(cap.Start) + " --> " + TimeSRT(cap.End) + eol
for _, line := range cap.Text {
res += line + eol
}
return res + eol
}
// TimeSRT renders a timestamp for use in .srt
func TimeSRT(t time.Time) string {
res := t.Format("15:04:05.000")
return strings.Replace(res, ".", ",", 1)
}