189 lines
4.3 KiB
Go
189 lines
4.3 KiB
Go
package lrc2srt
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"github.com/Hami-Lemon/lrc2srt/glist"
|
||
"io"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
type SRTMergeMode int
|
||
|
||
const (
|
||
SRT_MERGE_MODE_STACK SRTMergeMode = iota
|
||
SRT_MERGE_MODE_UP
|
||
SRT_MERGE_MODE_BOTTOM
|
||
)
|
||
|
||
type SRTContent struct {
|
||
//序号,从1开始
|
||
Index int
|
||
//开始时间,单位毫秒
|
||
Start int
|
||
//结束时间,单位毫秒
|
||
End int
|
||
//歌词内容
|
||
Text string
|
||
}
|
||
|
||
/**
|
||
1
|
||
00:00:01,111 --> 00:00:10,111
|
||
字幕
|
||
|
||
*/
|
||
//返回SRT文件中,一句字幕的字符串表示形式
|
||
func (s *SRTContent) String() string {
|
||
builder := strings.Builder{}
|
||
builder.WriteString(strconv.Itoa(s.Index))
|
||
builder.WriteByte('\n')
|
||
sh, sm, ss, sms := millisecond2Time(s.Start)
|
||
eh, em, es, ems := millisecond2Time(s.End)
|
||
builder.WriteString(fmt.Sprintf("%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n",
|
||
sh, sm, ss, sms, eh, em, es, ems))
|
||
builder.WriteString(s.Text)
|
||
builder.WriteString("\n\n")
|
||
return builder.String()
|
||
}
|
||
|
||
type SRT struct {
|
||
//歌曲名
|
||
Title string
|
||
//歌手名 未指定文件名是,文件名格式为:歌曲名-歌手名.srt
|
||
Artist string
|
||
Content glist.Queue[*SRTContent]
|
||
}
|
||
|
||
// LrcToSrt LRC对象转换成SRT对象
|
||
func LrcToSrt(lrc *LRC) *SRT {
|
||
if lrc == nil {
|
||
return nil
|
||
}
|
||
srt := &SRT{
|
||
Title: lrc.Title,
|
||
Artist: lrc.Artist,
|
||
Content: glist.NewLinkedList[*SRTContent](),
|
||
}
|
||
index := 1
|
||
//上一条srt信息
|
||
var prevSRT *SRTContent
|
||
for it := lrc.LrcList.Iterator(); it.Has(); {
|
||
lrcNode := it.Next()
|
||
srtContent := &SRTContent{
|
||
Index: index,
|
||
Start: lrcNode.time,
|
||
Text: lrcNode.content,
|
||
}
|
||
if index != 1 {
|
||
//上一条歌词的结束时间设置为当前歌词的开始时间
|
||
prevSRT.End = srtContent.Start
|
||
}
|
||
srt.Content.PushBack(srtContent)
|
||
index++
|
||
prevSRT = srtContent
|
||
}
|
||
//最后一条歌词
|
||
if prevSRT != nil {
|
||
//结束时间是为其 开始时间+10 秒
|
||
prevSRT.End = prevSRT.Start + 1000
|
||
}
|
||
return srt
|
||
}
|
||
|
||
// Merge 将另一个srt信息合并到当前srt中,有三种合并模式
|
||
//1. SRT_MERGE_MODE_STACK: 按照开始时间对两个srt信息进行排序,交错合并
|
||
//2. SRT_MERGE_MODE_UP: 当前srt信息排列在上,另一个排列在下,即 other 追加到后面
|
||
//3. SRT_MERGE_MODE_BOTTOM: 当前srt信息排列在下,另一个排列在上,即 other 添加到前面
|
||
func (s *SRT) Merge(other *SRT, mode SRTMergeMode) {
|
||
switch mode {
|
||
case SRT_MERGE_MODE_STACK:
|
||
s.mergeStack(other)
|
||
case SRT_MERGE_MODE_UP:
|
||
s.mergeUp(other)
|
||
case SRT_MERGE_MODE_BOTTOM:
|
||
s.mergeBottom(other)
|
||
}
|
||
}
|
||
|
||
//可以类比为合并两个有序链表,
|
||
//算法参考:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
|
||
func (s *SRT) mergeStack(other *SRT) {
|
||
ls, _ := s.Content.(*glist.LinkedList[*SRTContent])
|
||
lo, _ := other.Content.(*glist.LinkedList[*SRTContent])
|
||
lhs, lho := ls.First, lo.First
|
||
//临时的空结点
|
||
preHead := &glist.Node[*SRTContent]{}
|
||
prev := preHead
|
||
|
||
for lhs != nil && lho != nil {
|
||
//副本结点,从而不改变other对象中的内容
|
||
oCopy := lho.Clone()
|
||
|
||
if lhs.Element.Start <= oCopy.Element.Start {
|
||
prev.Next = lhs
|
||
lhs.Prev = prev
|
||
lhs = lhs.Next
|
||
} else {
|
||
prev.Next = oCopy
|
||
oCopy.Prev = prev
|
||
lho = lho.Next
|
||
}
|
||
prev = prev.Next
|
||
}
|
||
if lhs == nil {
|
||
//如果剩下的内容是other中的,则依次迭代复制到s中
|
||
for n := lho; n != nil; n = n.Next {
|
||
c := n.Clone()
|
||
prev.Next = c
|
||
c.Prev = prev
|
||
prev = prev.Next
|
||
}
|
||
} else {
|
||
prev.Next = lhs
|
||
}
|
||
ls.First = preHead.Next
|
||
ls.First.Prev = nil
|
||
}
|
||
|
||
func (s *SRT) mergeUp(other *SRT) {
|
||
if other.Content.IsNotEmpty() {
|
||
for it := other.Content.Iterator(); it.Has(); {
|
||
s.Content.Append(it.Next())
|
||
}
|
||
}
|
||
}
|
||
|
||
func (s *SRT) mergeBottom(other *SRT) {
|
||
oq := other.Content
|
||
if oq.IsNotEmpty() {
|
||
s.Content.PushFront(*(oq.PullBack()))
|
||
}
|
||
}
|
||
|
||
// WriteFile 将SRT格式的数据写入指定的文件中
|
||
func (s *SRT) WriteFile(path string) error {
|
||
f, err := os.Create(path)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
err = s.Write(f)
|
||
err = f.Close()
|
||
return err
|
||
}
|
||
|
||
// Write 将SRT格式的数据写入dst中
|
||
func (s *SRT) Write(dst io.Writer) error {
|
||
//6KB的缓冲
|
||
bufSize := 1024 * 6
|
||
writer := bufio.NewWriterSize(dst, bufSize)
|
||
for it := s.Content.Iterator(); it.Has(); {
|
||
_, err := writer.WriteString(it.Next().String())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return writer.Flush()
|
||
}
|