Przeglądaj źródła

正在尝试修复超时控制导致的 chrome brower 释放问题

Signed-off-by: allan716 <[email protected]>
allan716 4 lat temu
rodzic
commit
7e5f89bd04

+ 1 - 0
.gitignore

@@ -22,3 +22,4 @@
 /TestData/hotfix/001/test
 /internal/pkg/hot_fix/settings.db
 /TestData/sub_helper/test
+/settings.db

+ 98 - 0
TestCode/test_timeout.go

@@ -0,0 +1,98 @@
+package TestCode
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/rod_helper"
+	"github.com/go-rod/rod/lib/proto"
+	"github.com/panjf2000/ants/v2"
+	"golang.org/x/net/context"
+	"sync"
+	"time"
+)
+
+func DownloadTest() error {
+
+	testFunc := func(i interface{}) error {
+		inData := i.(InputData)
+		defer func() {
+			println(inData.Index, "testFunc done.")
+		}()
+		println(inData.Index, "start...")
+
+		browser, err := rod_helper.NewBrowser("")
+		if err != nil {
+			return err
+		}
+		defer func() {
+			browser.Close()
+			println(inData.Index, "browser closed")
+		}()
+
+		page, err := rod_helper.NewPageNavigate(browser, "https://www.baidu.com", 1*time.Second, 5)
+		if err != nil {
+			return err
+		}
+		page.MustSetUserAgent(&proto.NetworkSetUserAgentOverride{
+			UserAgent: pkg.RandomUserAgent(true),
+		})
+		err = page.WaitLoad()
+		time.Sleep(6 * time.Second)
+
+		return nil
+	}
+
+	antPool, err := ants.NewPoolWithFunc(2, func(inData interface{}) {
+		data := inData.(InputData)
+		defer data.Wg.Done()
+		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+		defer cancel()
+
+		done := make(chan error, 1)
+		panicChan := make(chan interface{}, 1)
+		go func() {
+			defer func() {
+				if p := recover(); p != nil {
+					panicChan <- p
+				}
+			}()
+
+			done <- testFunc(inData)
+		}()
+
+		select {
+		case err := <-done:
+			if err != nil {
+				println("done with Error", err.Error())
+			}
+			return
+		case p := <-panicChan:
+			println("got panic", p)
+		case <-ctx.Done():
+			println("got time out", ctx.Err())
+			return
+		}
+	})
+	if err != nil {
+		return err
+	}
+	defer antPool.Release()
+	wg := sync.WaitGroup{}
+
+	for i := 0; i < 10; i++ {
+		wg.Add(1)
+		err = antPool.Invoke(InputData{Index: i, Wg: &wg})
+		if err != nil {
+			println("antPool.Invoke", err)
+		}
+	}
+	wg.Wait()
+
+	println("All Done.")
+
+	return nil
+}
+
+type InputData struct {
+	Index int
+	Wg    *sync.WaitGroup
+}

+ 11 - 0
TestCode/test_timeout_test.go

@@ -0,0 +1,11 @@
+package TestCode
+
+import "testing"
+
+func TestDownloadTest(t *testing.T) {
+
+	err := DownloadTest()
+	if err != nil {
+		t.Fatal(err)
+	}
+}

+ 54 - 25
internal/downloader.go

@@ -12,7 +12,6 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/zimuku"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/language"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/types"
@@ -26,7 +25,6 @@ import (
 	"os"
 	"path"
 	"path/filepath"
-	"strings"
 	"sync"
 )
 
@@ -162,7 +160,12 @@ func (d Downloader) DownloadSub4Movie(dir string) error {
 			d.log.Errorln("subSupplierHub.DownloadSub4Movie", inData.OneVideoFullPath, err)
 			return err
 		}
-		if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
+		// 返回的两个值都是 nil 的时候,就是无需下载字幕,那么同样不用输出额外的信息,因为之前会输出跳过的原因
+		if organizeSubFiles == nil {
+			return nil
+		}
+		// 去搜索了没有发现字幕
+		if len(organizeSubFiles) < 1 {
 			d.log.Infoln("no sub found", filepath.Base(inData.OneVideoFullPath))
 			return nil
 		}
@@ -171,7 +174,7 @@ func (d Downloader) DownloadSub4Movie(dir string) error {
 
 		return nil
 	}
-	p, err := ants.NewPoolWithFunc(d.reqParam.Threads, func(inData interface{}) {
+	antPool, err := ants.NewPoolWithFunc(d.reqParam.Threads, func(inData interface{}) {
 		data := inData.(InputData)
 		defer data.Wg.Done()
 		ctx, cancel := context.WithTimeout(context.Background(), common.OneVideoProcessTimeOut)
@@ -190,7 +193,10 @@ func (d Downloader) DownloadSub4Movie(dir string) error {
 		}()
 
 		select {
-		case _ = <-done:
+		case err := <-done:
+			if err != nil {
+				d.log.Errorln("DownloadSub4Movie.NewPoolWithFunc done with Error", err.Error())
+			}
 			return
 		case p := <-panicChan:
 			d.log.Errorln("DownloadSub4Movie.NewPoolWithFunc got panic", p)
@@ -198,17 +204,16 @@ func (d Downloader) DownloadSub4Movie(dir string) error {
 			d.log.Errorln("DownloadSub4Movie.NewPoolWithFunc got time out", ctx.Err())
 			return
 		}
-
 	})
 	if err != nil {
 		return err
 	}
-	defer p.Release()
+	defer antPool.Release()
 	wg := sync.WaitGroup{}
 	// 一个视频文件同时多个站点查询,阻塞完毕后,在进行下一个
 	for i, oneVideoFullPath := range d.movieFileFullPathList {
 		wg.Add(1)
-		err = p.Invoke(InputData{OneVideoFullPath: oneVideoFullPath, Index: i, Wg: &wg})
+		err = antPool.Invoke(InputData{OneVideoFullPath: oneVideoFullPath, Index: i, Wg: &wg})
 		if err != nil {
 			d.log.Errorln("DownloadSub4Movie ants.Invoke", err)
 		}
@@ -293,7 +298,7 @@ func (d Downloader) DownloadSub4Series(dir string) error {
 
 		return nil
 	}
-	p, err := ants.NewPoolWithFunc(d.reqParam.Threads, func(inData interface{}) {
+	antPool, err := ants.NewPoolWithFunc(d.reqParam.Threads, func(inData interface{}) {
 		data := inData.(InputData)
 		defer data.Wg.Done()
 		ctx, cancel := context.WithTimeout(context.Background(), common.OneVideoProcessTimeOut)
@@ -312,7 +317,10 @@ func (d Downloader) DownloadSub4Series(dir string) error {
 		}()
 
 		select {
-		case _ = <-done:
+		case err := <-done:
+			if err != nil {
+				d.log.Errorln("DownloadSub4Series.NewPoolWithFunc done with Error", err.Error())
+			}
 			return
 		case p := <-panicChan:
 			d.log.Errorln("DownloadSub4Series.NewPoolWithFunc got panic", p)
@@ -324,7 +332,7 @@ func (d Downloader) DownloadSub4Series(dir string) error {
 	if err != nil {
 		return err
 	}
-	defer p.Release()
+	defer antPool.Release()
 
 	// 是否是通过 emby_helper api 获取的列表
 	var seriesDirList = make([]string, 0)
@@ -343,7 +351,7 @@ func (d Downloader) DownloadSub4Series(dir string) error {
 	wg := sync.WaitGroup{}
 	for i, oneSeriesPath := range seriesDirList {
 		wg.Add(1)
-		err = p.Invoke(InputData{OneVideoFullPath: oneSeriesPath, Index: i, Wg: &wg})
+		err = antPool.Invoke(InputData{OneVideoFullPath: oneSeriesPath, Index: i, Wg: &wg})
 		if err != nil {
 			d.log.Errorln("DownloadSub4Series ants.Invoke", err)
 		}
@@ -366,6 +374,18 @@ func (d Downloader) oneVideoSelectBestSub(oneVideoFullPath string, organizeSubFi
 		}
 	}
 	// -------------------------------------------------
+	/*
+		这里需要额外考虑一点,有可能当前目录已经有一个 .Default 标记的字幕了
+		那么下载字幕丢进来的时候就需要提前把这个字幕找出来,去除整个 .Default 标记
+		然后进行正常的下载,存储和替换字幕,最后将本次操作的第一次标记为 .Default
+	*/
+	// 不管是不是保存多个字幕,都要先扫描本地的字幕,进行 .Default 去除
+	// 这个视频的所有字幕,去除 .default 标记
+	err = sub_helper.SearchVideoMatchSubFileAndRemoveDefaultMark(oneVideoFullPath)
+	if err != nil {
+		// 找个错误可以忍
+		d.log.Errorln("SearchVideoMatchSubFileAndRemoveDefaultMark,", oneVideoFullPath, err)
+	}
 	if d.reqParam.SaveMultiSub == false {
 		// 选择最优的一个字幕
 		var finalSubFile *subparser.FileInfo
@@ -375,7 +395,7 @@ func (d Downloader) oneVideoSelectBestSub(oneVideoFullPath string, organizeSubFi
 			return
 		}
 		// 找到了,写入文件
-		err = d.writeSubFile2VideoPath(oneVideoFullPath, *finalSubFile, "")
+		err = d.writeSubFile2VideoPath(oneVideoFullPath, *finalSubFile, "", true)
 		if err != nil {
 			d.log.Errorln("SaveMultiSub:", d.reqParam.SaveMultiSub, "writeSubFile2VideoPath:", err)
 			return
@@ -387,15 +407,20 @@ func (d Downloader) oneVideoSelectBestSub(oneVideoFullPath string, organizeSubFi
 			d.log.Warnln("SelectEachSiteTop1SubFile found none sub file")
 			return
 		}
-
+		// 多网站 Top 1 字幕保存的时候,第一个设置为 Default 即可
 		for i, file := range finalSubFiles {
-			err = d.writeSubFile2VideoPath(oneVideoFullPath, file, siteNames[i])
+			setDefault := false
+			if i == 0 {
+				setDefault = true
+			}
+			err = d.writeSubFile2VideoPath(oneVideoFullPath, file, siteNames[i], setDefault)
 			if err != nil {
 				d.log.Errorln("SaveMultiSub:", d.reqParam.SaveMultiSub, "writeSubFile2VideoPath:", err)
 				return
 			}
 		}
 	}
+	// -------------------------------------------------
 }
 
 // saveFullSeasonSub 这里就需要单独存储到连续剧每一季的文件夹的特殊文件夹中
@@ -438,17 +463,21 @@ func (d Downloader) saveFullSeasonSub(seriesInfo *series.SeriesInfo, organizeSub
 	return fullSeasonSubDict
 }
 
-// 在前面需要进行语言的筛选、排序,这里仅仅是存储
-func (d Downloader) writeSubFile2VideoPath(videoFileFullPath string, finalSubFile subparser.FileInfo, extraSubPreName string) error {
+// 在前面需要进行语言的筛选、排序,这里仅仅是存储, extraSubPreName 这里传递是字幕的网站,有就认为是多字幕的存储。空就是单字幕,单字幕就可以setDefault
+func (d Downloader) writeSubFile2VideoPath(videoFileFullPath string, finalSubFile subparser.FileInfo, extraSubPreName string, setDefault bool) error {
 	videoRootPath := filepath.Dir(videoFileFullPath)
-	embyLanExtName := language.Lang2EmbyName(finalSubFile.Lang)
-	// 构建视频文件加 emby_helper 的字幕预研要求名称
-	videoFileNameWithOutExt := strings.ReplaceAll(filepath.Base(videoFileFullPath),
-		filepath.Ext(videoFileFullPath), "")
-	if extraSubPreName != "" {
-		extraSubPreName = "[" + extraSubPreName + "]"
-	}
-	subNewName := videoFileNameWithOutExt + embyLanExtName + extraSubPreName + finalSubFile.Ext
+
+	subNewName := sub_helper.GenerateMixSubName(videoFileFullPath, finalSubFile.Ext, finalSubFile.Lang, extraSubPreName, setDefault, false)
+
+	//embyLanExtName := language.Lang2EmbyNameOld(finalSubFile.Lang)
+	//// 构建视频文件加 emby_helper 的字幕预研要求名称
+	//videoFileNameWithOutExt := strings.ReplaceAll(filepath.Base(videoFileFullPath),
+	//	filepath.Ext(videoFileFullPath), "")
+	//if extraSubPreName != "" {
+	//	extraSubPreName = "[" + extraSubPreName + "]"
+	//}
+	//subNewName := videoFileNameWithOutExt + embyLanExtName + extraSubPreName + finalSubFile.Ext
+
 	desSubFullPath := path.Join(videoRootPath, subNewName)
 	// 最后写入字幕
 	err := utils.OutputFile(desSubFullPath, finalSubFile.Data)

+ 9 - 4
internal/logic/movie_helper/moviehelper.go

@@ -20,27 +20,32 @@ import (
 
 // OneMovieDlSubInAllSite 一部电影在所有的网站下载相应的字幕
 func OneMovieDlSubInAllSite(Suppliers []ifaces.ISupplier, oneVideoFullPath string, i int) []supplier.SubInfo {
+
+	defer func() {
+		log_helper.GetLogger().Infoln(i, "DlSub End", oneVideoFullPath)
+	}()
+
 	var outSUbInfos = make([]supplier.SubInfo, 0)
 	// 同时进行查询
 	subInfosChannel := make(chan []supplier.SubInfo)
-	log_helper.GetLogger().Infoln("DlSub Start", oneVideoFullPath)
+	log_helper.GetLogger().Infoln(i, "DlSub Start", oneVideoFullPath)
 	for _, oneSupplier := range Suppliers {
 		nowSupplier := oneSupplier
 		go func() {
 			subInfos, err := OneMovieDlSubInOneSite(oneVideoFullPath, i, nowSupplier)
 			if err != nil {
-				log_helper.GetLogger().Errorln(nowSupplier.GetSupplierName(), "oneMovieDlSubInOneSite", err)
+				log_helper.GetLogger().Errorln(i, nowSupplier.GetSupplierName(), "oneMovieDlSubInOneSite", err)
 			}
 			subInfosChannel <- subInfos
 		}()
 	}
 	for i := 0; i < len(Suppliers); i++ {
 		v, ok := <-subInfosChannel
-		if ok == true {
+		if ok == true && v != nil {
 			outSUbInfos = append(outSUbInfos, v...)
 		}
 	}
-	log_helper.GetLogger().Infoln("DlSub End", oneVideoFullPath)
+
 	return outSUbInfos
 }
 

+ 8 - 7
internal/logic/series_helper/seriesHelper.go

@@ -146,15 +146,15 @@ func SkipChineseSeries(seriesRootPath string, _reqParam ...types.ReqParam) (bool
 // OneSeriesDlSubInAllSite 一部连续剧在所有的网站下载相应的字幕
 func OneSeriesDlSubInAllSite(Suppliers []ifaces.ISupplier, seriesInfo *series.SeriesInfo, i int) []supplier.SubInfo {
 	defer func() {
-		log_helper.GetLogger().Infoln("DlSub End", seriesInfo.DirPath)
+		log_helper.GetLogger().Infoln(i, "DlSub End", seriesInfo.DirPath)
 	}()
-	log_helper.GetLogger().Infoln("DlSub Start", seriesInfo.DirPath)
+	log_helper.GetLogger().Infoln(i, "DlSub Start", seriesInfo.DirPath)
 	log_helper.GetLogger().Debugln(seriesInfo.Name, "IMDB ID:", seriesInfo.ImdbId, "NeedDownloadSubs:", len(seriesInfo.NeedDlEpsKeyList))
 	var outSUbInfos = make([]supplier.SubInfo, 0)
 	if len(seriesInfo.NeedDlEpsKeyList) < 1 {
 		return outSUbInfos
 	}
-	for key, _ := range seriesInfo.NeedDlEpsKeyList {
+	for key := range seriesInfo.NeedDlEpsKeyList {
 		log_helper.GetLogger().Debugln(key)
 	}
 	// 同时进行查询
@@ -162,24 +162,25 @@ func OneSeriesDlSubInAllSite(Suppliers []ifaces.ISupplier, seriesInfo *series.Se
 	for _, oneSupplier := range Suppliers {
 		nowSupplier := oneSupplier
 		go func() {
+			var subInfos []supplier.SubInfo
 			defer func() {
+				subInfosChannel <- subInfos
 				log_helper.GetLogger().Infoln(i, nowSupplier.GetSupplierName(), "End...")
 			}()
+
 			log_helper.GetLogger().Infoln(i, nowSupplier.GetSupplierName(), "Start...")
 			// 一次性把这一部连续剧的所有字幕下载完
 			subInfos, err := nowSupplier.GetSubListFromFile4Series(seriesInfo)
 			if err != nil {
-				log_helper.GetLogger().Errorln(nowSupplier.GetSupplierName(), "GetSubListFromFile4Series", err)
+				log_helper.GetLogger().Errorln(i, nowSupplier.GetSupplierName(), "GetSubListFromFile4Series", err)
 			}
 			// 把后缀名给改好
 			sub_helper.ChangeVideoExt2SubExt(subInfos)
-
-			subInfosChannel <- subInfos
 		}()
 	}
 	for i := 0; i < len(Suppliers); i++ {
 		v, ok := <-subInfosChannel
-		if ok == true {
+		if ok == true && v != nil {
 			outSUbInfos = append(outSUbInfos, v...)
 		}
 	}

+ 16 - 19
internal/logic/sub_supplier/subhd/subhd.go

@@ -67,6 +67,12 @@ func (s Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo,
 
 func (s Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
 
+	browser, err := rod_helper.NewBrowser(s.reqParam.HttpProxy)
+	if err != nil {
+		return nil, err
+	}
+	defer browser.Close()
+
 	var subInfos = make([]supplier.SubInfo, 0)
 	var subList = make([]HdListItem, 0)
 	for value := range seriesInfo.NeedDlSeasonDict {
@@ -105,13 +111,6 @@ func (s Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]su
 	// 找到那些 Eps 需要下载字幕的
 	subInfoNeedDownload := s.whichEpisodeNeedDownloadSub(seriesInfo, subList)
 	// 下载字幕
-	var browser *rod.Browser
-	// 是用本地的 Browser 还是远程的,推荐是远程的
-	browser, err := rod_helper.NewBrowser(s.reqParam.HttpProxy)
-	if err != nil {
-		return nil, err
-	}
-	defer browser.Close()
 	for i, item := range subInfoNeedDownload {
 		hdContent, err := s.step2Ex(browser, item.Url)
 		if err != nil {
@@ -133,6 +132,12 @@ func (s Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]sup
 }
 
 func (s Supplier) getSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
+
+	browser, err := rod_helper.NewBrowser(s.reqParam.HttpProxy)
+	if err != nil {
+		return nil, err
+	}
+	defer browser.Close()
 	/*
 		虽然是传入视频文件路径,但是其实需要读取对应的视频文件目录下的
 		movie.xml 以及 *.nfo,找到 IMDB id
@@ -156,7 +161,7 @@ func (s Supplier) getSubListFromFile4Movie(filePath string) ([]supplier.SubInfo,
 
 	if imdbInfo.ImdbId != "" {
 		// 先用 imdb id 找
-		subInfoList, err = s.getSubListFromKeyword4Movie(imdbInfo.ImdbId)
+		subInfoList, err = s.getSubListFromKeyword4Movie(browser, imdbInfo.ImdbId)
 		if err != nil {
 			// 允许的错误,跳过,继续进行文件名的搜索
 			s.log.Errorln(s.GetSupplierName(), "keyword:", imdbInfo.ImdbId)
@@ -169,7 +174,7 @@ func (s Supplier) getSubListFromFile4Movie(filePath string) ([]supplier.SubInfo,
 	}
 	// 如果没有,那么就用文件名查找
 	searchKeyword := pkg.VideoNameSearchKeywordMaker(info.Title, imdbInfo.Year)
-	subInfoList, err = s.getSubListFromKeyword4Movie(searchKeyword)
+	subInfoList, err = s.getSubListFromKeyword4Movie(browser, searchKeyword)
 	if err != nil {
 		s.log.Errorln(s.GetSupplierName(), "keyword:", searchKeyword)
 		return nil, err
@@ -178,7 +183,7 @@ func (s Supplier) getSubListFromFile4Movie(filePath string) ([]supplier.SubInfo,
 	return subInfoList, nil
 }
 
-func (s Supplier) getSubListFromKeyword4Movie(keyword string) ([]supplier.SubInfo, error) {
+func (s Supplier) getSubListFromKeyword4Movie(browser *rod.Browser, keyword string) ([]supplier.SubInfo, error) {
 
 	var subInfos []supplier.SubInfo
 	detailPageUrl, err := s.step0(keyword)
@@ -194,19 +199,11 @@ func (s Supplier) getSubListFromKeyword4Movie(keyword string) ([]supplier.SubInf
 		return nil, err
 	}
 
-	var browser *rod.Browser
-	// 是用本地的 Browser 还是远程的,推荐是远程的
-	browser, err = rod_helper.NewBrowser(s.reqParam.HttpProxy)
-	if err != nil {
-		return nil, err
-	}
-	defer browser.Close()
-
 	for i, item := range subList {
 		hdContent, err := s.step2Ex(browser, item.Url)
 		time.Sleep(time.Second)
 		if err != nil {
-			s.log.Errorln("step2Ex", err)
+			s.log.Errorln("subhd step2Ex", err)
 			return nil, err
 		}
 		subInfos = append(subInfos, *supplier.NewSubInfo(s.GetSupplierName(), int64(i), hdContent.Filename, types.ChineseSimple, pkg.AddBaseUrl(common.SubSubHDRootUrl, item.Url), 0, 0, hdContent.Ext, hdContent.Data))

+ 34 - 2
internal/pkg/language/language.go

@@ -100,8 +100,8 @@ func IsBilingualSubtitle(lan types.Language) bool {
 	}
 }
 
-// Lang2EmbyName 从语言转换到 Emby 能够识别的字幕命名
-func Lang2EmbyName(lan types.Language) string {
+// Lang2EmbyNameOld 弃用。从语言转换到 Emby 能够识别的字幕命名
+func Lang2EmbyNameOld(lan types.Language) string {
 	switch lan {
 	case types.Unknow: // 未知语言
 		return types.Emby_unknow
@@ -132,6 +132,38 @@ func Lang2EmbyName(lan types.Language) string {
 	}
 }
 
+// Lang2ChineseString 将 types.Language 转换为中文描述:简、繁、简英
+func Lang2ChineseString(lan types.Language) string {
+	switch lan {
+	case types.Unknow: // 未知语言
+		return types.MathLangChnUnknow
+	case types.ChineseSimple: // 简体中文
+		return types.MatchLangChs
+	case types.ChineseTraditional: // 繁体中文
+		return types.MatchLangCht
+	case types.ChineseSimpleEnglish: // 简英双语字幕
+		return types.MatchLangChsEn
+	case types.ChineseTraditionalEnglish: // 繁英双语字幕
+		return types.MatchLangChtEn
+	case types.English: // 英文
+		return types.MatchLangEn
+	case types.Japanese: // 日语
+		return types.MatchLangJp
+	case types.ChineseSimpleJapanese: // 简日双语字幕
+		return types.MatchLangChsJp
+	case types.ChineseTraditionalJapanese: // 繁日双语字幕
+		return types.MatchLangChtJp
+	case types.Korean: // 韩语
+		return types.MatchLangKr
+	case types.ChineseSimpleKorean: // 简韩双语字幕
+		return types.MatchLangChsKr
+	case types.ChineseTraditionalKorean: // 繁韩双语字幕
+		return types.MatchLangChtKr
+	default:
+		return types.MathLangChnUnknow
+	}
+}
+
 // GetLangOptions 语言识别的 Options Whitelist
 func GetLangOptions() whatlanggo.Options {
 	return whatlanggo.Options{

+ 68 - 0
internal/pkg/sub_helper/sub_helper.go

@@ -230,6 +230,50 @@ func SearchMatchedSubFile(dir string) ([]string, error) {
 	return fileFullPathList, nil
 }
 
+// SearchVideoMatchSubFileAndRemoveDefaultMark 找到找个视频目录下相匹配的字幕,同时去除这些字幕中 .default 的标记
+func SearchVideoMatchSubFileAndRemoveDefaultMark(oneVideoFullPath string) error {
+
+	dir := path.Dir(oneVideoFullPath)
+	fileName := filepath.Base(oneVideoFullPath)
+	fileName = strings.ToLower(fileName)
+	fileName = strings.ReplaceAll(fileName, filepath.Ext(fileName), "")
+	pathSep := string(os.PathSeparator)
+	files, err := ioutil.ReadDir(dir)
+	if err != nil {
+		return err
+	}
+	for _, curFile := range files {
+		if curFile.IsDir() {
+			continue
+		} else {
+			// 这里就是文件了
+			if curFile.Size() < 1000 {
+				continue
+			}
+			// 后缀名得对
+			if IsSubExtWanted(filepath.Ext(curFile.Name())) == false {
+				continue
+			}
+			// 字幕文件名应该包含 视频文件名(无后缀)
+			if strings.Contains(curFile.Name(), fileName) == false {
+				continue
+			}
+			// 得包含 .default. 找个关键词
+			if strings.Contains(curFile.Name(), types.Emby_default+".") == false {
+				continue
+			}
+			oldPath := dir + pathSep + curFile.Name()
+			newPath := dir + pathSep + strings.ReplaceAll(curFile.Name(), types.Emby_default+".", ".")
+			err = os.Rename(oldPath, newPath)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
 // IsOldVersionSubPrefixName 是否是老版本的字幕命名 .chs_en[shooter] ,符合也返回这个部分+字幕格式后缀名 .chs_en[shooter].ass, 修改后的名称
 func IsOldVersionSubPrefixName(subFileName string) (bool, string, string) {
 
@@ -323,6 +367,30 @@ func IsOldVersionSubPrefixName(subFileName string) (bool, string, string) {
 	return false, "", ""
 }
 
+// GenerateMixSubName 这里会生成类似的文件名 xxxx.chinese(中英,shooter)
+func GenerateMixSubName(videoFileName, subExt string, subLan types.Language, extraSubPreName string, setDefault, setForced bool) string {
+
+	videoFileNameWithOutExt := strings.ReplaceAll(filepath.Base(videoFileName),
+		filepath.Ext(videoFileName), "")
+
+	note := ""
+	if extraSubPreName != "" {
+		note = "," + extraSubPreName
+	}
+
+	appendString := ""
+	if setDefault == true {
+		appendString = ".default"
+	}
+	if setForced == true {
+		appendString = ".forced"
+	}
+
+	subNewName := videoFileNameWithOutExt + "(" + language.Lang2ChineseString(subLan) + note + ")" + appendString + subExt
+
+	return subNewName
+}
+
 func makeMixSubExtString(orgFileNameWithOutExt, lang string, ext, site string, beDefault bool) string {
 
 	tmpDefault := ""

+ 12 - 1
main.go

@@ -45,7 +45,18 @@ func main() {
 	// ------ 数据库相关操作 End ------
 
 	// ------ Hot Fix Start ------
-
+	// 开始修复
+	//log.Infoln("HotFix Start...")
+	//err = hot_fix.HotFixProcess(types.HotFixParam{
+	//	MovieRootDir:  config.MovieFolder,
+	//	SeriesRootDir: config.SeriesFolder,
+	//})
+	//if err != nil {
+	//	log.Errorln("HotFixProcess()", err)
+	//	log.Infoln("HotFix End")
+	//	return
+	//}
+	//log.Infoln("HotFix End")
 	// ------ Hot Fix End ------
 
 	// 初始化通知缓存模块