all repos — gopipe @ aeba99cbfb8bc6d8fe24e936e223a4ad9ee1815f

Embed YouTube videos on Telegram, Discord and more!

handle auto-generated subtitles
Marco Andronaco andronacomarco@gmail.com
Thu, 17 Oct 2024 10:00:52 +0200
commit

aeba99cbfb8bc6d8fe24e936e223a4ad9ee1815f

parent

67133f23417228de33b3483dd9c9559082403ae0

2 files changed, 55 insertions(+), 23 deletions(-)

jump to
M src/app/proxy.gosrc/app/proxy.go

@@ -103,13 +103,7 @@ defer res.Body.Close()

w.Header().Set("Content-Type", "text/vtt") - content, err := io.ReadAll(res.Body) - if err != nil { - http.Error(w, err500, http.StatusInternalServerError) - return - } - - err = subs.Parse(content, w) + err = subs.Convert(res.Body, w) if err != nil { http.Error(w, err500, http.StatusInternalServerError) return
M src/subs/subs.gosrc/subs/subs.go

@@ -3,8 +3,9 @@

import ( "encoding/xml" "fmt" + "io" "log" - "net/http" + "strings" ) type TimedText struct {

@@ -17,9 +18,28 @@ Paragraphs []Paragraph `xml:"p"`

} type Paragraph struct { - Text string `xml:",chardata"` - Start int `xml:"t,attr"` // Start time in milliseconds - Length int `xml:"d,attr"` // Duration in milliseconds + Start int `xml:"t,attr"` // Start time in milliseconds + Length int `xml:"d,attr"` // Duration in milliseconds + Text string `xml:",chardata"` // Direct text (for cases without <s> tags) + Sentences []Sentence `xml:"s"` // List of <s> tags (for cases with individual words/phrases) +} + +type Sentence struct { + Text string `xml:",chardata"` // Text inside the <s> tag + Time int `xml:"t,attr"` // Optional start time (not always present) +} + +func writeVTT(output io.Writer, i, startTime, endTime int, sentence string) { + output.Write( + []byte( + fmt.Sprintf( + "%d\n%s --> %s\n%s\n\n", + i, + millisecondsToTimestamp(startTime), + millisecondsToTimestamp(endTime), + sentence, + ), + )) } // Convert milliseconds to WebVTT timestamp format: HH:MM:SS.mmm

@@ -29,28 +49,46 @@ milliseconds := ms % 1000

return fmt.Sprintf("%02d:%02d:%02d.%03d", seconds/3600, (seconds%3600)/60, seconds%60, milliseconds) } -func writeString(s string, w http.ResponseWriter) { - w.Write([]byte(s)) -} +func Convert(reader io.Reader, output io.Writer) error { + content, err := io.ReadAll(reader) + if err != nil { + return err + } -func Parse(inputData []byte, w http.ResponseWriter) error { var timedText TimedText - err := xml.Unmarshal(inputData, &timedText) + err = xml.Unmarshal(content, &timedText) if err != nil { log.Println("Error unmarshalling XML:", err) return err } - // Write the WebVTT header - writeString("WEBVTT\n\n", w) + output.Write([]byte("WEBVTT\n\n")) - // Loop through the paragraphs and write them to the WebVTT file + var lastEndTime int for i, p := range timedText.Body.Paragraphs { - startTime := millisecondsToTimestamp(p.Start) - endTime := millisecondsToTimestamp(p.Start + p.Length) + startTimeMS := p.Start + endTimeMS := p.Start + p.Length + + if startTimeMS < lastEndTime { + startTimeMS = lastEndTime + } + + var sentence string + if len(p.Sentences) > 0 { + for _, s := range p.Sentences { + sentence += s.Text + } + } else { + sentence = p.Text + } + + sentence = strings.TrimSpace(sentence) + if sentence == "" { + continue + } - s := fmt.Sprintf("%d\n%s --> %s\n%s\n\n", i+1, startTime, endTime, p.Text) - writeString(s, w) + lastEndTime = endTimeMS + writeVTT(output, i+1, startTimeMS, endTimeMS, sentence) } return nil