sub-cli/internal/format/srt/srt.go
2025-04-23 08:01:13 +08:00

137 lines
3.2 KiB
Go

package srt
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
"sub-cli/internal/model"
)
// Parse parses an SRT file and returns a slice of SRTEntries
func Parse(filePath string) ([]model.SRTEntry, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var entries []model.SRTEntry
var currentEntry model.SRTEntry
var isContent bool
var contentBuffer strings.Builder
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
if currentEntry.Number != 0 {
currentEntry.Content = contentBuffer.String()
entries = append(entries, currentEntry)
currentEntry = model.SRTEntry{}
isContent = false
contentBuffer.Reset()
}
continue
}
if currentEntry.Number == 0 {
currentEntry.Number, _ = strconv.Atoi(line)
} else if isEntryTimeStampUnset(currentEntry) {
times := strings.Split(line, " --> ")
if len(times) == 2 {
currentEntry.StartTime = parseSRTTimestamp(times[0])
currentEntry.EndTime = parseSRTTimestamp(times[1])
isContent = true
}
} else if isContent {
if contentBuffer.Len() > 0 {
contentBuffer.WriteString("\n")
}
contentBuffer.WriteString(line)
}
}
// Don't forget the last entry
if currentEntry.Number != 0 && contentBuffer.Len() > 0 {
currentEntry.Content = contentBuffer.String()
entries = append(entries, currentEntry)
}
if err := scanner.Err(); err != nil {
return nil, err
}
return entries, nil
}
// isEntryTimeStampUnset checks if timestamp is unset
func isEntryTimeStampUnset(entry model.SRTEntry) bool {
return entry.StartTime.Hours == 0 &&
entry.StartTime.Minutes == 0 &&
entry.StartTime.Seconds == 0 &&
entry.StartTime.Milliseconds == 0
}
// parseSRTTimestamp parses an SRT timestamp string into a Timestamp struct
func parseSRTTimestamp(timeStr string) model.Timestamp {
timeStr = strings.Replace(timeStr, ",", ".", 1)
format := "15:04:05.000"
t, err := time.Parse(format, timeStr)
if err != nil {
return model.Timestamp{}
}
return model.Timestamp{
Hours: t.Hour(),
Minutes: t.Minute(),
Seconds: t.Second(),
Milliseconds: t.Nanosecond() / 1000000,
}
}
// Generate generates an SRT file from a slice of SRTEntries
func Generate(entries []model.SRTEntry, filePath string) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()
for _, entry := range entries {
fmt.Fprintf(file, "%d\n", entry.Number)
fmt.Fprintf(file, "%s --> %s\n",
formatSRTTimestamp(entry.StartTime),
formatSRTTimestamp(entry.EndTime))
fmt.Fprintf(file, "%s\n\n", entry.Content)
}
return nil
}
// formatSRTTimestamp formats a Timestamp struct as an SRT timestamp string
func formatSRTTimestamp(ts model.Timestamp) string {
return fmt.Sprintf("%02d:%02d:%02d,%03d",
ts.Hours,
ts.Minutes,
ts.Seconds,
ts.Milliseconds)
}
// ConvertToLyrics converts SRT entries to a Lyrics structure
func ConvertToLyrics(entries []model.SRTEntry) model.Lyrics {
lyrics := model.Lyrics{
Metadata: make(map[string]string),
}
for _, entry := range entries {
lyrics.Timeline = append(lyrics.Timeline, entry.StartTime)
lyrics.Content = append(lyrics.Content, entry.Content)
}
return lyrics
}