loadtup-dl/writesubs.go

108 lines
3.1 KiB
Go

package main
import (
"fmt"
"io"
"sort"
"time"
)
func secs_to_ass_time(secs float64) string {
dur := time.Duration(secs) * time.Second
hh := int64(dur.Hours())
mm := int64(dur.Minutes()) - (hh * 60)
ss := int64(dur.Seconds()) - (hh * 3600) - (mm * 60)
ms := int64(dur.Milliseconds()) - (hh * 3600000) - (mm * 60000) - (ss * 1000)
return fmt.Sprintf("%02d:%02d:%02d.%03d", hh, mm, ss, ms)
}
// WriteSubtitle streams the video subtitles to the supplied writer in ASS format.
func (ltc *loadTupContent) WriteSubtitle(w io.Writer, totalVideoDurationSecs float64) error {
w.Write([]byte(`[Script Info]
; Script generated by loadtup-dl
ScriptType: v4.00+
Collisions: Normal
Timer: 100,0000
WrapStyle: 3
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default, Arial,16,&H00FFFFFF,&H00FFFFFF,&H00000008,&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,2,10,10,10,0
Style: TLNote, Arial,10,&H00FFFFFF,&H00FFFFFF,&H00000008,&H80000008,-1,0,0,0,100,100,0.00,0.00,1,1.00,2.00,8,10,10,10,0
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
`))
type entry struct {
startTime float64
entry string
}
entries := make([]entry, 0, len(ltc.Caps)+len(ltc.Scrcaps))
for i := 0; i < len(ltc.Caps); i += 1 {
if ltc.Caps[i] == "" {
// Don't show anything
continue
}
start := secs_to_ass_time(ltc.Secs[i])
var end string
if i < len(ltc.Caps)-1 {
end = secs_to_ass_time(ltc.Secs[i+1])
} else {
// The final subtitle. Loadtup displays these for the entire
// remaining video duration
end = secs_to_ass_time(totalVideoDurationSecs)
}
// Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
entries = append(entries, entry{
startTime: ltc.Secs[i],
entry: fmt.Sprintf("Dialogue: 0,%s,%s,Default,,0000,0000,0000,,%s\n", start, end, ltc.Caps[i]),
})
}
// Repeat for scrcaps, using top positioning and a different layer
for i := 0; i < len(ltc.Scrcaps); i += 1 {
if ltc.Caps[i] == "" {
// Don't show anything
continue
}
start := secs_to_ass_time(ltc.Scrsecs[i])
var end string
if i < len(ltc.Scrcaps)-1 {
end = secs_to_ass_time(ltc.Scrsecs[i+1])
} else {
// The final subtitle. Loadtup displays these for the entire
// remaining video duration
end = secs_to_ass_time(totalVideoDurationSecs)
}
// Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
entries = append(entries, entry{
startTime: ltc.Scrsecs[i],
entry: fmt.Sprintf("Dialogue: 1,%s,%s,TLNote,,0000,0000,0000,,%s\n", start, end, ltc.Scrcaps[i]),
})
}
// Sort all the entries by their start time, to mingle TL note entries
// properly with the other subtitles
sort.SliceStable(entries, func(i, j int) bool {
return entries[i].startTime < entries[j].startTime
})
// Emit all to writer
for _, e := range entries {
w.Write([]byte(e.entry))
}
return nil
}