108 lines
3.1 KiB
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
|
|
}
|