mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Caption support (#2462)
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
159
vendor/github.com/asticode/go-astisub/srt.go
generated
vendored
Normal file
159
vendor/github.com/asticode/go-astisub/srt.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package astisub
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
srtTimeBoundariesSeparator = " --> "
|
||||
)
|
||||
|
||||
// Vars
|
||||
var (
|
||||
bytesSRTTimeBoundariesSeparator = []byte(srtTimeBoundariesSeparator)
|
||||
)
|
||||
|
||||
// parseDurationSRT parses an .srt duration
|
||||
func parseDurationSRT(i string) (time.Duration, error) {
|
||||
return parseDuration(i, ",", 3)
|
||||
}
|
||||
|
||||
// ReadFromSRT parses an .srt content
|
||||
func ReadFromSRT(i io.Reader) (o *Subtitles, err error) {
|
||||
// Init
|
||||
o = NewSubtitles()
|
||||
var scanner = bufio.NewScanner(i)
|
||||
|
||||
// Scan
|
||||
var line string
|
||||
var lineNum int
|
||||
var s = &Item{}
|
||||
for scanner.Scan() {
|
||||
// Fetch line
|
||||
line = strings.TrimSpace(scanner.Text())
|
||||
lineNum++
|
||||
|
||||
// Remove BOM header
|
||||
if lineNum == 1 {
|
||||
line = strings.TrimPrefix(line, string(BytesBOM))
|
||||
}
|
||||
|
||||
// Line contains time boundaries
|
||||
if strings.Contains(line, srtTimeBoundariesSeparator) {
|
||||
// Return the wrong number of rows
|
||||
if len(s.Lines) == 0 {
|
||||
err = fmt.Errorf("astisub: line %d: no lines", lineNum)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove last item of previous subtitle since it's the index
|
||||
index := s.Lines[len(s.Lines)-1]
|
||||
s.Lines = s.Lines[:len(s.Lines)-1]
|
||||
|
||||
// Remove trailing empty lines
|
||||
if len(s.Lines) > 0 {
|
||||
for i := len(s.Lines) - 1; i >= 0; i-- {
|
||||
if len(s.Lines[i].Items) > 0 {
|
||||
for j := len(s.Lines[i].Items) - 1; j >= 0; j-- {
|
||||
if len(s.Lines[i].Items[j].Text) == 0 {
|
||||
s.Lines[i].Items = s.Lines[i].Items[:j]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(s.Lines[i].Items) == 0 {
|
||||
s.Lines = s.Lines[:i]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init subtitle
|
||||
s = &Item{}
|
||||
|
||||
// Fetch Index
|
||||
s.Index, _ = strconv.Atoi(index.String())
|
||||
|
||||
// Extract time boundaries
|
||||
s1 := strings.Split(line, srtTimeBoundariesSeparator)
|
||||
if l := len(s1); l < 2 {
|
||||
err = fmt.Errorf("astisub: line %d: time boundaries has only %d element(s)", lineNum, l)
|
||||
return
|
||||
}
|
||||
// We do this to eliminate extra stuff like positions which are not documented anywhere
|
||||
s2 := strings.Split(s1[1], " ")
|
||||
|
||||
// Parse time boundaries
|
||||
if s.StartAt, err = parseDurationSRT(s1[0]); err != nil {
|
||||
err = fmt.Errorf("astisub: line %d: parsing srt duration %s failed: %w", lineNum, s1[0], err)
|
||||
return
|
||||
}
|
||||
if s.EndAt, err = parseDurationSRT(s2[0]); err != nil {
|
||||
err = fmt.Errorf("astisub: line %d: parsing srt duration %s failed: %w", lineNum, s2[0], err)
|
||||
return
|
||||
}
|
||||
|
||||
// Append subtitle
|
||||
o.Items = append(o.Items, s)
|
||||
} else {
|
||||
// Add text
|
||||
s.Lines = append(s.Lines, Line{Items: []LineItem{{Text: strings.TrimSpace(line)}}})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// formatDurationSRT formats an .srt duration
|
||||
func formatDurationSRT(i time.Duration) string {
|
||||
return formatDuration(i, ",", 3)
|
||||
}
|
||||
|
||||
// WriteToSRT writes subtitles in .srt format
|
||||
func (s Subtitles) WriteToSRT(o io.Writer) (err error) {
|
||||
// Do not write anything if no subtitles
|
||||
if len(s.Items) == 0 {
|
||||
err = ErrNoSubtitlesToWrite
|
||||
return
|
||||
}
|
||||
|
||||
// Add BOM header
|
||||
var c []byte
|
||||
c = append(c, BytesBOM...)
|
||||
|
||||
// Loop through subtitles
|
||||
for k, v := range s.Items {
|
||||
// Add time boundaries
|
||||
c = append(c, []byte(strconv.Itoa(k+1))...)
|
||||
c = append(c, bytesLineSeparator...)
|
||||
c = append(c, []byte(formatDurationSRT(v.StartAt))...)
|
||||
c = append(c, bytesSRTTimeBoundariesSeparator...)
|
||||
c = append(c, []byte(formatDurationSRT(v.EndAt))...)
|
||||
c = append(c, bytesLineSeparator...)
|
||||
|
||||
// Loop through lines
|
||||
for _, l := range v.Lines {
|
||||
c = append(c, []byte(l.String())...)
|
||||
c = append(c, bytesLineSeparator...)
|
||||
}
|
||||
|
||||
// Add new line
|
||||
c = append(c, bytesLineSeparator...)
|
||||
}
|
||||
|
||||
// Remove last new line
|
||||
c = c[:len(c)-1]
|
||||
|
||||
// Write
|
||||
if _, err = o.Write(c); err != nil {
|
||||
err = fmt.Errorf("astisub: writing failed: %w", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user