123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- package subparser
- import (
- "crypto/sha256"
- "fmt"
- "math"
- "sort"
- "strings"
- "time"
- "github.com/ChineseSubFinder/ChineseSubFinder/pkg"
- "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/common"
- "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/language"
- )
- type FileInfo struct {
- PrefixDialogueString string // 在 Dialogue: 这个关键词之前的字符串,ass 中的字体以及其他信息的描述
- Content string // 字幕的内容
- FromWhereSite string // 从那个网站下载的
- Name string // 字幕的名称,注意,这里需要额外的赋值,不会自动检测
- Ext string // 字幕的后缀名
- Lang language.MyLanguage // 识别出来的语言
- FileFullPath string // 字幕文件的全路径
- Data []byte // 字幕的二进制文件内容
- Dialogues []OneDialogue // 整个字幕文件的所有对话,如果是做时间轴匹配,就使用原始的
- DialoguesFilter []OneDialogue // 整个字幕文件的所有对话,过滤掉特殊字符的对白
- DialoguesFilterEx []OneDialogueEx // 整个字幕文件的所有对话,过滤掉特殊字符的对白,这里会把一句话中支持的 中、英、韩、日 四国语言给分离出来
- CHLines []string // 抽取出所有的中文对话
- OtherLines []string // 抽取出所有的第二语言对话,可能是英文、韩文、日文
- }
- // SaveTranslated 保存字幕文件,注意,这里是用于翻译后的字幕文件
- func (f *FileInfo) SaveTranslated(desSubFileFPath string) error {
- allString := ""
- allString += f.PrefixDialogueString + "\n"
- for _, oneDialogue := range f.Dialogues {
- if len(oneDialogue.Lines) < 1 {
- continue
- }
- oneDialogueString := "Dialogue: 0," + oneDialogue.StartTime + "," + oneDialogue.EndTime + ",Default,,0,0,0,," + oneDialogue.Lines[0]
- allString += oneDialogueString + "\n"
- }
- return pkg.WriteFile(desSubFileFPath, []byte(allString))
- }
- // GetSourceTranslateString 获取翻以前的字符串,会移除 \N 这样的信息,替换为空格
- func (f *FileInfo) GetSourceTranslateString() string {
- sourceString := ""
- // 去除每一句中的 \N
- for index, oneDialogue := range f.Dialogues {
- f.Dialogues[index].Lines[0] = strings.ReplaceAll(oneDialogue.Lines[0], `\N`, " ")
- sourceString += f.Dialogues[index].Lines[0] + "\n"
- }
- return sourceString
- }
- func (f *FileInfo) SetTranslatedStrings(translatedString string) error {
- // 分行
- lines := strings.Split(translatedString, "\n")
- linesWithOutEmpty := make([]string, 0)
- // 移除空行
- for _, line := range lines {
- if len(line) < 1 {
- continue
- }
- linesWithOutEmpty = append(linesWithOutEmpty, line)
- }
- // 比较两个数组是否长度一致
- if len(f.Dialogues) != len(linesWithOutEmpty) {
- return fmt.Errorf("dialogue line not the same,org:%d,translated:%d", len(f.Dialogues), len(linesWithOutEmpty))
- }
- // 对每一句话进行赋值
- for index := range f.Dialogues {
- f.Dialogues[index].Lines = []string{linesWithOutEmpty[index]}
- }
- return nil
- }
- // SortDialogues 排序对话,时间递减
- func (f *FileInfo) SortDialogues() {
- sort.Sort(OneDialogueByStartTime(f.Dialogues))
- sort.Sort(OneDialogueByStartTime(f.DialoguesFilter))
- sort.Sort(OneDialogueByStartTimeEx(f.DialoguesFilterEx))
- }
- // GetTimeFormat 获取时间轴的格式化格式
- func (f FileInfo) GetTimeFormat() string {
- if f.Ext == common.SubExtASS || f.Ext == common.SubExtSSA {
- return common.TimeFormatPoint2
- } else {
- return common.TimeFormatPoint3
- }
- }
- // GetDialogueExContent 获取当前字幕文件语言对应索引的对白内容
- // 凡是带有 Eng 的返回 Eng,其他的就与对应语言相关
- func (f FileInfo) GetDialogueExContent(index int) string {
- switch f.Lang {
- case language.ChineseSimple, language.ChineseTraditional,
- language.ChineseSimpleJapanese, language.ChineseSimpleKorean,
- language.ChineseTraditionalJapanese, language.ChineseTraditionalKorean:
- // 带有中文的,但是又不是中英的
- return f.DialoguesFilterEx[index].ChLine
- case language.English, language.ChineseSimpleEnglish, language.ChineseTraditionalEnglish:
- return f.DialoguesFilterEx[index].EnLine
- case language.Japanese:
- return f.DialoguesFilterEx[index].JpLine
- case language.Korean:
- return f.DialoguesFilterEx[index].KrLine
- default:
- return f.DialoguesFilterEx[index].EnLine
- }
- }
- // ChangeDialoguesTimeByFramerateRatio 根据帧数比率调整时间轴 对应 ffsubsync -- SubtitleScaler
- func (f *FileInfo) ChangeDialoguesTimeByFramerateRatio(framerateRatio float64) error {
- timeFormat := f.GetTimeFormat()
- f.changeOneDialoguesFramerateRatio(f.Dialogues, framerateRatio, timeFormat)
- f.changeOneDialoguesFramerateRatio(f.DialoguesFilter, framerateRatio, timeFormat)
- f.changeOneDialogueExsFramerateRatio(f.DialoguesFilterEx, framerateRatio, timeFormat)
- return nil
- }
- func (f *FileInfo) changeOneDialoguesFramerateRatio(oneDialogues []OneDialogue, framerateRatio float64, timeFormat string) {
- for i := 0; i < len(oneDialogues); i++ {
- timeStart := oneDialogues[i].GetStartTime()
- timeEnd := oneDialogues[i].GetEndTime()
- timeStartNumber := pkg.Time2SecondNumber(timeStart)
- timeEndNumber := pkg.Time2SecondNumber(timeEnd)
- scaleTimeStart := pkg.TimeNumber2Time(timeStartNumber * framerateRatio)
- scaleTimeEnd := pkg.TimeNumber2Time(timeEndNumber * framerateRatio)
- oneDialogues[i].StartTime = pkg.Time2SubTimeString(scaleTimeStart, timeFormat)
- oneDialogues[i].EndTime = pkg.Time2SubTimeString(scaleTimeEnd, timeFormat)
- }
- }
- func (f *FileInfo) changeOneDialogueExsFramerateRatio(oneDialogues []OneDialogueEx, framerateRatio float64, timeFormat string) {
- for i := 0; i < len(oneDialogues); i++ {
- timeStart := oneDialogues[i].GetStartTime()
- timeEnd := oneDialogues[i].GetEndTime()
- timeStartNumber := pkg.Time2SecondNumber(timeStart)
- timeEndNumber := pkg.Time2SecondNumber(timeEnd)
- scaleTimeStart := pkg.TimeNumber2Time(timeStartNumber * framerateRatio)
- scaleTimeEnd := pkg.TimeNumber2Time(timeEndNumber * framerateRatio)
- oneDialogues[i].StartTime = pkg.Time2SubTimeString(scaleTimeStart, timeFormat)
- oneDialogues[i].EndTime = pkg.Time2SubTimeString(scaleTimeEnd, timeFormat)
- }
- }
- // GetStartTime 获取的是从 Dialogues 得到的
- func (f FileInfo) GetStartTime() time.Time {
- startTime := math.MaxFloat64
- for i := 0; i < len(f.Dialogues); i++ {
- // 找到最小的开始时间
- tmpNowStartTimeNumber := pkg.Time2SecondNumber(f.Dialogues[i].GetStartTime())
- startTime = math.Min(startTime, tmpNowStartTimeNumber)
- }
- return pkg.TimeNumber2Time(startTime)
- }
- // GetEndTime 获取的是从 Dialogues 得到的
- func (f FileInfo) GetEndTime() time.Time {
- endTime := -math.MaxFloat64
- for i := 0; i < len(f.Dialogues); i++ {
- // 找到最大的结束时间
- tmpNowEndTimeNumber := pkg.Time2SecondNumber(f.Dialogues[i].GetEndTime())
- endTime = math.Max(endTime, tmpNowEndTimeNumber)
- }
- return pkg.TimeNumber2Time(endTime)
- }
- // GetNumFrames 获取这个字幕的时间 Frame 数量
- func (f FileInfo) GetNumFrames() int {
- return int(math.Abs((pkg.Time2SecondNumber(f.GetEndTime()) - pkg.Time2SecondNumber(f.GetStartTime())) * 100))
- }
- func (f FileInfo) GetFileSha256() string {
- return fmt.Sprintf("%x", sha256.Sum256(f.Data))
- }
- // OneDialogue 一句对话
- type OneDialogue struct {
- Index int // 对白的索引
- StartTime string // 开始时间
- EndTime string // 结束时间
- StyleName string // StyleName
- Lines []string // 台词
- }
- func NewOneDialogue() OneDialogue {
- return OneDialogue{
- Lines: make([]string, 0),
- }
- }
- func (o OneDialogue) GetStartTime() time.Time {
- srcTimeStartNow, err := pkg.ParseTime(o.StartTime)
- if err != nil {
- return time.Time{}
- }
- return srcTimeStartNow
- }
- func (o OneDialogue) GetEndTime() time.Time {
- srcTimeEndNow, err := pkg.ParseTime(o.EndTime)
- if err != nil {
- return time.Time{}
- }
- return srcTimeEndNow
- }
- type OneDialogueByStartTime []OneDialogue
- func (d OneDialogueByStartTime) Len() int {
- return len(d)
- }
- func (d OneDialogueByStartTime) Swap(i, j int) {
- d[i], d[j] = d[j], d[i]
- }
- func (d OneDialogueByStartTime) Less(i, j int) bool {
- subStartTimeI, err := pkg.ParseTime(d[i].StartTime)
- if err != nil {
- return false
- }
- subStartTimeJ, err := pkg.ParseTime(d[j].StartTime)
- if err != nil {
- return false
- }
- return pkg.Time2SecondNumber(subStartTimeI) < pkg.Time2SecondNumber(subStartTimeJ)
- }
- // OneDialogueEx 一句对话,这里会把一句话中支持的 中、英、韩、日 四国语言给分离出来
- type OneDialogueEx struct {
- StartTime string // 开始时间
- EndTime string // 结束时间
- ChLine string
- EnLine string
- KrLine string
- JpLine string
- }
- func (o OneDialogueEx) GetStartTime() time.Time {
- srcTimeStartNow, err := pkg.ParseTime(o.StartTime)
- if err != nil {
- return time.Time{}
- }
- return srcTimeStartNow
- }
- func (o OneDialogueEx) GetEndTime() time.Time {
- srcTimeEndNow, err := pkg.ParseTime(o.EndTime)
- if err != nil {
- return time.Time{}
- }
- return srcTimeEndNow
- }
- type OneDialogueByStartTimeEx []OneDialogueEx
- func (d OneDialogueByStartTimeEx) Len() int {
- return len(d)
- }
- func (d OneDialogueByStartTimeEx) Swap(i, j int) {
- d[i], d[j] = d[j], d[i]
- }
- func (d OneDialogueByStartTimeEx) Less(i, j int) bool {
- subStartTimeI, err := pkg.ParseTime(d[i].StartTime)
- if err != nil {
- return false
- }
- subStartTimeJ, err := pkg.ParseTime(d[j].StartTime)
- if err != nil {
- return false
- }
- return pkg.Time2SecondNumber(subStartTimeI) < pkg.Time2SecondNumber(subStartTimeJ)
- }
- const (
- Sub_Ext_Mark_Default = ".default" // 指定这个字幕是默认的
- Sub_Ext_Mark_Forced = ".forced" // 指定这个字幕是强制的
- )
|