feat: vtt converting

This commit is contained in:
CDN 2025-04-23 10:44:08 +08:00
parent ba2e477dc0
commit ba66894e42
Signed by: CDN
GPG key ID: 0C656827F9F80080
7 changed files with 693 additions and 107 deletions

View file

@ -3,12 +3,13 @@ package converter
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sub-cli/internal/format/lrc"
"sub-cli/internal/format/srt"
"sub-cli/internal/format/txt"
"sub-cli/internal/format/vtt"
"sub-cli/internal/model"
)
@ -20,118 +21,47 @@ func Convert(sourceFile, targetFile string) error {
sourceFmt := strings.TrimPrefix(filepath.Ext(sourceFile), ".")
targetFmt := strings.TrimPrefix(filepath.Ext(targetFile), ".")
switch sourceFmt {
// TXT only supports being a target format
if sourceFmt == "txt" {
return fmt.Errorf("%w: txt is only supported as a target format", ErrUnsupportedFormat)
}
// Convert source to intermediate representation
subtitle, err := convertToIntermediate(sourceFile, sourceFmt)
if err != nil {
return err
}
// Convert from intermediate representation to target format
return convertFromIntermediate(subtitle, targetFile, targetFmt)
}
// convertToIntermediate converts a source file to our intermediate Subtitle representation
func convertToIntermediate(sourceFile, sourceFormat string) (model.Subtitle, error) {
switch sourceFormat {
case "lrc":
return convertFromLRC(sourceFile, targetFile, targetFmt)
return lrc.ConvertToSubtitle(sourceFile)
case "srt":
return convertFromSRT(sourceFile, targetFile, targetFmt)
return srt.ConvertToSubtitle(sourceFile)
case "vtt":
return vtt.ConvertToSubtitle(sourceFile)
default:
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, sourceFmt)
return model.Subtitle{}, fmt.Errorf("%w: %s", ErrUnsupportedFormat, sourceFormat)
}
}
// convertFromLRC converts an LRC file to another format
func convertFromLRC(sourceFile, targetFile, targetFmt string) error {
sourceLyrics, err := lrc.Parse(sourceFile)
if err != nil {
return fmt.Errorf("error parsing source LRC file: %w", err)
}
switch targetFmt {
// convertFromIntermediate converts our intermediate Subtitle representation to a target format
func convertFromIntermediate(subtitle model.Subtitle, targetFile, targetFormat string) error {
switch targetFormat {
case "lrc":
return lrc.ConvertFromSubtitle(subtitle, targetFile)
case "srt":
return srt.ConvertFromSubtitle(subtitle, targetFile)
case "vtt":
return vtt.ConvertFromSubtitle(subtitle, targetFile)
case "txt":
return lrcToTxt(sourceLyrics, targetFile)
case "srt":
return lrcToSRT(sourceLyrics, targetFile)
case "lrc":
return lrc.Generate(sourceLyrics, targetFile)
return txt.GenerateFromSubtitle(subtitle, targetFile)
default:
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, targetFmt)
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, targetFormat)
}
}
// convertFromSRT converts an SRT file to another format
func convertFromSRT(sourceFile, targetFile, targetFmt string) error {
entries, err := srt.Parse(sourceFile)
if err != nil {
return fmt.Errorf("error parsing source SRT file: %w", err)
}
switch targetFmt {
case "txt":
return srtToTxt(entries, targetFile)
case "lrc":
lyrics := srt.ConvertToLyrics(entries)
return lrc.Generate(lyrics, targetFile)
case "srt":
return srt.Generate(entries, targetFile)
default:
return fmt.Errorf("%w: %s", ErrUnsupportedFormat, targetFmt)
}
}
// lrcToTxt converts LRC lyrics to a plain text file
func lrcToTxt(lyrics model.Lyrics, targetFile string) error {
file, err := os.Create(targetFile)
if err != nil {
return fmt.Errorf("error creating target file: %w", err)
}
defer file.Close()
for _, content := range lyrics.Content {
if _, err := fmt.Fprintln(file, content); err != nil {
return err
}
}
return nil
}
// lrcToSRT converts LRC lyrics to an SRT file
func lrcToSRT(lyrics model.Lyrics, targetFile string) error {
var entries []model.SRTEntry
for i, content := range lyrics.Content {
if i >= len(lyrics.Timeline) {
break
}
startTime := lyrics.Timeline[i]
endTime := startTime
// If there's a next timeline entry, use it for end time
// Otherwise add a few seconds to the start time
if i+1 < len(lyrics.Timeline) {
endTime = lyrics.Timeline[i+1]
} else {
endTime.Seconds += 3
}
entry := model.SRTEntry{
Number: i + 1,
StartTime: startTime,
EndTime: endTime,
Content: content,
}
entries = append(entries, entry)
}
return srt.Generate(entries, targetFile)
}
// srtToTxt converts SRT entries to a plain text file
func srtToTxt(entries []model.SRTEntry, targetFile string) error {
file, err := os.Create(targetFile)
if err != nil {
return fmt.Errorf("error creating target file: %w", err)
}
defer file.Close()
for _, entry := range entries {
if _, err := fmt.Fprintln(file, entry.Content); err != nil {
return err
}
}
return nil
}