diff --git a/README.md b/README.md index 943646b..a9edf59 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 这是一个可以识别视频语音自动生成字幕SRT文件的开源软件工具。
适用于快速、批量的为媒体(视频/音频)生成中/英文字幕、文本文件的业务场景。 -0.3.1 版本将会使用以下接口: +0.3.2 版本将会使用以下接口: - 阿里云 [OSS对象存储](https://www.aliyun.com/product/oss?spm=5176.12825654.eofdhaal5.13.e9392c4aGfj5vj&aly_as=K11FcpO8) - 阿里云 [录音文件识别](https://ai.aliyun.com/nls/filetrans?spm=5176.12061031.1228726.1.47fe3cb43I34mn) - 百度翻译开放平台 [翻译API](http://api.fanyi.baidu.com/api/trans/product/index) @@ -14,8 +14,12 @@ CLI(命令行)版本:[https://github.com/wxbool/video-srt](https://github. 软件帮助文档/使用教程看这个:[https://www.yuque.com/viggo-t7cdi/videosrt](https://www.yuque.com/viggo-t7cdi/videosrt) +B站Up主自制教程:[https://search.bilibili.com/all?keyword=videosrt](https://search.bilibili.com/all?keyword=videosrt) + 线上“字幕生成/字幕翻译”解决方案:[字幕酱(付费)](https://www.zimujiang.com/aff?code=aannv4os) +线上“文字配音/字幕配音/文章转视频”解决方案:[幕言](https://www.mu-yan.com/?videosrt) + ## 界面预览 @@ -46,8 +50,8 @@ CLI(命令行)版本:[https://github.com/wxbool/video-srt](https://github. ##### 下载地址: -- (v0.3.1)(含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.3.1/video-srt-gui-ffmpeg-0.3.1-x64.zip) -- (v0.3.1)(不含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.3.1/video-srt-gui-0.3.1-x64.zip) +- (v0.3.2)(含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.3.2/video-srt-gui-ffmpeg-0.3.2-x64.zip) +- (v0.3.2)(不含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.3.2/video-srt-gui-0.3.2-x64.zip) - (v0.2.6)(含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.2.6/video-srt-gui-ffmpeg-0.2.6-x64.zip) - (v0.2.6)(不含ffmpeg依赖) [点我下载](http://file.viggo.site/video-srt/0.2.6/video-srt-gui-0.2.6-x64.zip) diff --git a/app/aliyun/tool.go b/app/aliyun/tool.go index 35e0662..1690fbd 100644 --- a/app/aliyun/tool.go +++ b/app/aliyun/tool.go @@ -83,10 +83,13 @@ func AliyunAudioResultWordHandle(result [] byte , callback func (vresult *Aliyun } - var symbol = []string{"?","。",",","!",";","?",".",",","!"} + var symbol = []string{"?","。",",","!",";","、","?",".",",","!"} //数据集处理 for _ , value := range audioResult { for _ , data := range value { + // filter + data.Text = FilterText(data.Text) + data.Blocks = GetTextBlock(data.Text) data.Text = ReplaceStrs(data.Text , symbol , "") @@ -125,11 +128,20 @@ func AliyunAudioResultWordHandle(result [] byte , callback func (vresult *Aliyun if ischinese { block += word.Word if tool.CheckChineseNumber(word.Word) && FindSliceIntCount(chineseNumberWordIndexs , i) == 0 { - cl := tool.ChineseNumberToLowercaseLength(word.Word) - - if (cl - utf8.RuneCountInString(word.Word)) > 0 { - chineseNumberDiffLength += (cl - utf8.RuneCountInString(word.Word)) + cl := tool.ChineseNumberToLowercaseLength(word.Word) - utf8.RuneCountInString(word.Word) + if cl > 0 { + chineseNumberDiffLength += cl chineseNumberWordIndexs = append(chineseNumberWordIndexs , i) + } else { + //例外 + if i != 0 { + newWord := value[i-1].Word + word.Word + cl := tool.ChineseNumberToLowercaseLength(newWord) - utf8.RuneCountInString(newWord) + if cl > 0 { + chineseNumberDiffLength += cl + chineseNumberWordIndexs = append(chineseNumberWordIndexs , i) + } + } } } } else { @@ -157,11 +169,11 @@ func AliyunAudioResultWordHandle(result [] byte , callback func (vresult *Aliyun if ((blockRune >= B) || (blockRune + chineseNumberDiffLength >= B)) && B != -1 { flag = true - //fmt.Println( w.Blocks ) + //fmt.Println(w.Blocks) //fmt.Println(B , lastBlock , (B - lastBlock) , word.Word) //fmt.Println(w.Text) //fmt.Println( block ) - //fmt.Println("\n\n\n") + //fmt.Println("\n") var thisText = "" //容错机制 @@ -353,7 +365,7 @@ func IndexRunes(strs string , olds []rune) int { } func GetTextBlock(strs string) ([]int) { - var symbol_zhcn = []rune{'?','。',',','!',';','?','.',',','!'} + var symbol_zhcn = []rune{'?','。',',','!',';','、','?','.',',','!'} //var symbol_en = []rune{'?','.',',','!'} strsRune := []rune(strs) @@ -390,3 +402,12 @@ func SubString(str string , begin int ,length int) (substr string) { // 返回子串 return string(rs[begin:end]) } + + +//过滤文本 +func FilterText(text string) string { + //去除换行符 + re, _ := regexp.Compile("[\n|\r|\r\n]+") + text = re.ReplaceAllString(text, "") + return text +} \ No newline at end of file diff --git a/app/data.go b/app/data.go index ee01859..a609b1e 100644 --- a/app/data.go +++ b/app/data.go @@ -138,6 +138,7 @@ type AppSetings struct { OutputLanguage int //输出字幕语言 OutputMainSubtitleInputLanguage bool //双语主字幕(输入语言) + CloseIntelligentBlockSwitch bool //关闭智能分段 CloseNewVersionMessage bool //关闭软件新版本提醒(默认开启)[false开启 true关闭] CloseAutoDeleteOssTempFile bool //关闭自动删除临时音频文件(默认开启)[false开启 true关闭] } diff --git a/app/event.go b/app/event.go index 016703e..5ed8d44 100644 --- a/app/event.go +++ b/app/event.go @@ -46,6 +46,7 @@ func(mw *MyMainWindow) RunAppSetingDialog(owner walk.Form , confirmCall func(*Ap Dialog{ AssignTo: &dlg, Title: "软件设置", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -82,6 +83,12 @@ func(mw *MyMainWindow) RunAppSetingDialog(owner walk.Form , confirmCall func(*Ap }, + Label{ + Text: "关闭软件智能分段处理:", + }, + CheckBox{ + Checked: Bind("CloseIntelligentBlockSwitch"), + }, Label{ Text: "关闭OSS临时文件清理:", }, @@ -153,6 +160,7 @@ func(mw *MyMainWindow) RunSpeechEngineSetingDialog(owner walk.Form , confirmCall Dialog{ AssignTo: &dlg, Title: "新建语音引擎", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -295,6 +303,7 @@ func(mw *MyMainWindow) RunBaiduTranslateEngineSetingDialog(owner walk.Form , con Dialog{ AssignTo: &dlg, Title: "新建翻译引擎(百度翻译)", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -424,6 +433,7 @@ func(mw *MyMainWindow) RunTengxunyunTranslateEngineSetingDialog(owner walk.Form Dialog{ AssignTo: &dlg, Title: "新建翻译引擎(腾讯云)", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -543,6 +553,7 @@ func (mw *MyMainWindow) RunObjectStorageSetingDialog(owner walk.Form) { Dialog{ AssignTo: &dlg, Title: "OSS对象存储设置", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -670,6 +681,7 @@ func (mw *MyMainWindow) RunGlobalFilterSetingDialog (owner walk.Form , historyWo Dialog{ AssignTo: &dlg, Title: "全局语气词过滤设置", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ @@ -837,6 +849,7 @@ func (mw *MyMainWindow) RunDefinedFilterSetingDialog (owner walk.Form , historyR Dialog{ AssignTo: &dlg, Title: "自定义过滤设置", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, MinSize: Size{600, 500}, @@ -955,6 +968,7 @@ func (mw *MyMainWindow) RunNewDefinedFilterRuleDialog (owner walk.Form , copyRow Dialog{ AssignTo: &dlg, Title: "新增自定义过滤规则", + Font:Font{Family: "微软雅黑", PointSize: 9}, DefaultButton: &acceptPB, CancelButton: &cancelPB, DataBinder: DataBinder{ diff --git a/app/tool/chinese_simple.go b/app/tool/chinese_simple.go index 26f726d..5f0c66f 100644 --- a/app/tool/chinese_simple.go +++ b/app/tool/chinese_simple.go @@ -31,25 +31,29 @@ func ChineseNumberToLowercaseLength(s string) int { numberPosi := true maxBaseNumber := 1 - for i:=0; i 20 { + //输出比例 + app.Log("字幕翻译已处理:" + fmt.Sprintf("%.2f" , rv) + "%" , video) + lastrv = rv + } } } + } - for _ , data := range result { - transResult,e := app.RunTranslate(data.Text , video) - if e != nil { - return e - //panic(e) //终止翻译 + if app.OutputType.TXT { + for channel,result := range AudioResult { + soundChannel := channel + 1 + if outputSoundTrack != 3 && outputSoundTrack != 0 { + if outputSoundTrack != int(soundChannel) { + continue //跳出非输出音轨转换 + } } - data.TranslateText = strings.TrimSpace(transResult.TransResultDst) //译文 - index++ - rv := (float64(index)/float64(totalRow))*100 - if (rv - lastrv) > 20 { - //输出比例 - app.Log("字幕翻译已处理:" + fmt.Sprintf("%.2f" , rv) + "%" , video) - lastrv = rv + for _ , data := range result { + transResult,e := app.RunTranslate(data.Text , video) + if e != nil { + return e + //panic(e) //终止翻译 + } + data.TranslateText = strings.TrimSpace(transResult.TransResultDst) //译文 + + index++ + rv := (float64(index)/float64(totalRow))*100 + if (rv - lastrv) > 20 { + //输出比例 + app.Log("字幕翻译已处理:" + fmt.Sprintf("%.2f" , rv) + "%" , video) + lastrv = rv + } } } } - } + } else { //关闭智能分段 + for channel,result := range AudioResult { + soundChannel := channel + 1 + if outputSoundTrack != 3 && outputSoundTrack != 0 { + if outputSoundTrack != int(soundChannel) { + continue //跳出非输出音轨转换 + } + } + totalRow += len(result) + } - if app.OutputType.TXT { + index := 0 + //翻译任务 for channel,result := range AudioResult { soundChannel := channel + 1 if outputSoundTrack != 3 && outputSoundTrack != 0 { @@ -539,8 +610,6 @@ func AliyunAudioResultTranslate(app *VideoSrt , video string , AudioResult map[i //panic(e) //终止翻译 } data.TranslateText = strings.TrimSpace(transResult.TransResultDst) //译文 - - index++ rv := (float64(index)/float64(totalRow))*100 if (rv - lastrv) > 20 { @@ -639,12 +708,19 @@ func AliyunAudioResultMakeSubtitleFile(app *VideoSrt , video string , outputType //注入合适的数据块 thisAudioResult := make(map[int64][] *aliyun.AliyunAudioRecognitionResult) - if outputType == OUTPUT_SRT || outputType == OUTPUT_LRC { - thisAudioResult = IntelligentBlockResult - } else if outputType == OUTPUT_STRING { + + //开启智能分段 + if !app.CloseIntelligentBlockSwitch { + if outputType == OUTPUT_SRT || outputType == OUTPUT_LRC { + thisAudioResult = IntelligentBlockResult + } else if outputType == OUTPUT_STRING { + thisAudioResult = AudioResult + } + } else { thisAudioResult = AudioResult } + oneSoundChannel := false //是否输出单条音轨 //根据音轨,输出文件 for channel,result := range thisAudioResult { diff --git a/go.mod b/go.mod index 6fab679..f1f6a9a 100644 --- a/go.mod +++ b/go.mod @@ -3,21 +3,24 @@ module videosrt go 1.12 require ( + github.com/JamesHovious/w32 v1.1.0 + github.com/Onelio/winmm v0.0.0-20191219112928-4c30bd2f3ec1 github.com/PuerkitoBio/goquery v1.5.0 github.com/aliyun/alibaba-cloud-sdk-go v1.60.268 github.com/aliyun/aliyun-oss-go-sdk v2.0.4+incompatible github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect - github.com/bitly/go-simplejson v0.5.0 - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/buger/jsonparser v0.0.0-20191004114745-ee4c978eae7e github.com/dreamCodeMan/xfyun_go_sdk v0.0.0-20200227025001-249b66fa8600 - github.com/kr/pretty v0.2.0 // indirect + github.com/gen2brain/malgo v0.10.15 // indirect + github.com/hajimehoshi/go-mp3 v0.3.0 // indirect github.com/lxn/walk v0.0.0-20191121152919-b7c43041fb1b github.com/lxn/win v0.0.0-20191106123917-121afc750dd3 // indirect github.com/mewkiz/flac v1.0.6 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go v3.0.128+incompatible + github.com/youpy/go-riff v0.0.0-20131220112943-557d78c11efb // indirect + github.com/youpy/go-wav v0.0.0-20160223082350-b63a9887d320 // indirect golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect diff --git a/main.go b/main.go index b0fc0c0..6f0b690 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( ) //应用版本号 -const APP_VERSION = "0.3.1" +const APP_VERSION = "0.3.2" var AppRootDir string var mw *MyMainWindow @@ -108,6 +108,7 @@ func main() { AssignTo: &mw.MainWindow, Icon:"./data/img/index.png", Title: "VideoSrt - 一键字幕生成、字幕翻译小工具" + " - " + APP_VERSION, + Font:Font{Family: "微软雅黑", PointSize: 9}, ToolBar: ToolBar{ ButtonStyle: ToolBarButtonImageBeforeText, Items: []MenuItem{ @@ -255,6 +256,7 @@ func main() { appSetings.SrtFileDir = setings.SrtFileDir appSetings.CloseNewVersionMessage = setings.CloseNewVersionMessage appSetings.CloseAutoDeleteOssTempFile = setings.CloseAutoDeleteOssTempFile + appSetings.CloseIntelligentBlockSwitch = setings.CloseIntelligentBlockSwitch multitask.SetMaxConcurrencyNumber( setings.MaxConcurrency ) srtTranslateMultitask.SetMaxConcurrencyNumber( setings.MaxConcurrency ) @@ -305,13 +307,13 @@ func main() { Text: "语音合成配音/文章转视频", Image: "./data/img/muyan.png", OnTriggered: func() { - _ = tool.OpenUrl("http://www.mu-yan.net/") + _ = tool.OpenUrl("https://www.mu-yan.net/") }, }, }, }, - Size: Size{800, 600}, - MinSize: Size{300, 500}, + Size: Size{800, 650}, + MinSize: Size{300, 650}, Layout: VBox{}, Children: []Widget{ HSplitter{ @@ -728,9 +730,24 @@ func main() { tlens := len(taskFiles.Files) if tlens == 0 { - mw.NewErrormationTips("错误" , "请先拖入要处理的媒体文件") - return + //兼容外部调用 + tempDropFilesEdit := dropFilesEdit.Text() + if tempDropFilesEdit != "" { + tempFileLists := strings.Split(tempDropFilesEdit , "\r\n") + //检测文件列表 + tempResult , _ := VaildateHandleFiles(tempFileLists , true ,false) + if len(tempResult) != 0 { + taskFiles.Files = tempResult + dropFilesEdit.SetText(strings.Join(tempResult, "\r\n")) + } + } + + if len(taskFiles.Files) == 0 { + mw.NewErrormationTips("错误" , "请先拖入要处理的媒体文件") + return + } } + //校验文件列表 if _,e := VaildateHandleFiles(taskFiles.Files , true , false); e!=nil { mw.NewErrormationTips("错误" , e.Error()) @@ -756,8 +773,10 @@ func main() { if tempAppSetting.InputLanguage != LANGUAGE_ZH && tempAppSetting.InputLanguage != LANGUAGE_EN && tempAppSetting.InputLanguage != LANGUAGE_JP && + tempAppSetting.InputLanguage != LANGUAGE_KOR && + tempAppSetting.InputLanguage != LANGUAGE_RU && tempAppSetting.InputLanguage != LANGUAGE_SPA { - mw.NewErrormationTips("错误" , "由于语音提供商的限制,生成字幕仅支持中文、英文、日语、西班牙语") + mw.NewErrormationTips("错误" , "由于语音提供商的限制,生成字幕仅支持中文、英文、日语、韩语、俄语、西班牙语") return } @@ -808,6 +827,7 @@ func main() { videosrt.SetSoundTrack(appSetings.SoundTrack) videosrt.SetMaxConcurrency(appSetings.MaxConcurrency) videosrt.SetCloseAutoDeleteOssTempFile(appSetings.CloseAutoDeleteOssTempFile) + videosrt.SetCloseIntelligentBlockSwitch(appSetings.CloseIntelligentBlockSwitch) //设置输出文件 videosrt.SetOutputType(operateFrom.OutputType) @@ -887,7 +907,7 @@ func main() { //待处理的文件 tlens := len(taskFiles.Files) if tlens == 0 { - mw.NewErrormationTips("错误" , "请先拖入要处理的字幕文件") + mw.NewErrormationTips("错误" , "请先拖入需要处理的SRT字幕文件") return } //校验文件列表