Browse Source

重构,字幕格式化完成 emby 的实现

Signed-off-by: allan716 <[email protected]>
allan716 4 years ago
parent
commit
3c9679967d

+ 2 - 1
cmd/chinesesubfinder/main.go

@@ -7,6 +7,7 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/hot_fix"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/notify_center"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/emby"
 	"github.com/allanpk716/ChineseSubFinder/internal/types"
 	"github.com/robfig/cron/v3"
 	"github.com/sirupsen/logrus"
@@ -116,7 +117,7 @@ func DownLoadStart(httpProxy string) {
 	notify_center.Notify.Clear()
 
 	// 下载实例
-	downloader := internal.NewDownloader(types.ReqParam{
+	downloader := internal.NewDownloader(emby.NewFormatter(), types.ReqParam{
 		HttpProxy:                     httpProxy,
 		DebugMode:                     config.DebugMode,
 		SaveMultiSub:                  config.SaveMultiSub,

+ 7 - 4
internal/downloader.go

@@ -2,6 +2,7 @@ package internal
 
 import (
 	"github.com/allanpk716/ChineseSubFinder/internal/common"
+	"github.com/allanpk716/ChineseSubFinder/internal/ifaces"
 	embyHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/emby_helper"
 	markSystem "github.com/allanpk716/ChineseSubFinder/internal/logic/mark_system"
 	seriesHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/series_helper"
@@ -37,11 +38,13 @@ type Downloader struct {
 	embyHelper            *embyHelper.EmbyHelper
 	movieFileFullPathList []string                      //  多个需要搜索字幕的电影文件全路径
 	seriesSubNeedDlMap    map[string][]emby.EmbyMixInfo //  多个需要搜索字幕的连续剧目录
+	subFormatter          ifaces.ISubFormatter          //	字幕格式化命名的实现
 }
 
-func NewDownloader(_reqParam ...types.ReqParam) *Downloader {
+func NewDownloader(inSubFormatter ifaces.ISubFormatter, _reqParam ...types.ReqParam) *Downloader {
 
 	var downloader Downloader
+	downloader.subFormatter = inSubFormatter
 	downloader.log = log_helper.GetLogger()
 	downloader.topic = common.DownloadSubsPerSite
 	if len(_reqParam) > 0 {
@@ -387,10 +390,10 @@ func (d Downloader) oneVideoSelectBestSub(oneVideoFullPath string, organizeSubFi
 	*/
 	// 不管是不是保存多个字幕,都要先扫描本地的字幕,进行 .Default 去除
 	// 这个视频的所有字幕,去除 .default 标记
-	err = sub_helper.SearchVideoMatchSubFileAndRemoveDefaultMark(oneVideoFullPath)
+	err = sub_helper.SearchVideoMatchSubFileAndRemoveExtMark(oneVideoFullPath)
 	if err != nil {
 		// 找个错误可以忍
-		d.log.Errorln("SearchVideoMatchSubFileAndRemoveDefaultMark,", oneVideoFullPath, err)
+		d.log.Errorln("SearchVideoMatchSubFileAndRemoveExtMark,", oneVideoFullPath, err)
 	}
 	if d.reqParam.SaveMultiSub == false {
 		// 选择最优的一个字幕
@@ -473,7 +476,7 @@ func (d Downloader) saveFullSeasonSub(seriesInfo *series.SeriesInfo, organizeSub
 func (d Downloader) writeSubFile2VideoPath(videoFileFullPath string, finalSubFile subparser.FileInfo, extraSubPreName string, setDefault bool) error {
 
 	videoRootPath := filepath.Dir(videoFileFullPath)
-	subNewName, subNewNameWithDefault, _ := sub_helper.GenerateMixSubName(videoFileFullPath, finalSubFile.Ext, finalSubFile.Lang, extraSubPreName)
+	subNewName, subNewNameWithDefault, _ := d.subFormatter.GenerateMixSubName(videoFileFullPath, finalSubFile.Ext, finalSubFile.Lang, extraSubPreName)
 
 	desSubFullPath := path.Join(videoRootPath, subNewName)
 	if setDefault == true {

+ 4 - 3
internal/downloader_test.go

@@ -4,6 +4,7 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_parser/ass"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_parser/srt"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/emby"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/types"
 	"testing"
@@ -21,7 +22,7 @@ func TestDownloader_DownloadSub4Movie(t *testing.T) {
 	dirRoot := "X:\\电影\\The Boss Baby Family Business (2021)"
 	//dirRoot := "X:\\电影"
 	config := pkg.GetConfig()
-	dl := NewDownloader(types.ReqParam{
+	dl := NewDownloader(emby.NewFormatter(), types.ReqParam{
 		SaveMultiSub:    true,
 		SubTypePriority: 1,
 		EmbyConfig:      config.EmbyConfig,
@@ -53,7 +54,7 @@ func TestDownloader_DownloadSub4Series(t *testing.T) {
 
 	config := pkg.GetConfig()
 	// 如果需要调试 Emby 一定需要 dirRoot := "X:\\连续剧"
-	dl := NewDownloader(types.ReqParam{
+	dl := NewDownloader(emby.NewFormatter(), types.ReqParam{
 		SaveMultiSub:    true,
 		SubTypePriority: 1,
 		EmbyConfig:      config.EmbyConfig,
@@ -71,7 +72,7 @@ func TestDownloader_DownloadSub4Series(t *testing.T) {
 func TestDownloader_GetUpdateVideoListFromEmby(t *testing.T) {
 	var err error
 	config := pkg.GetConfig()
-	dl := NewDownloader(types.ReqParam{
+	dl := NewDownloader(emby.NewFormatter(), types.ReqParam{
 		SaveMultiSub:    true,
 		SubTypePriority: 1,
 		EmbyConfig:      config.EmbyConfig,

+ 5 - 2
internal/ifaces/iSubFormatter.go

@@ -3,7 +3,10 @@ package ifaces
 import "github.com/allanpk716/ChineseSubFinder/internal/types"
 
 type ISubFormatter interface {
-	IsMatchThisFormat(subName string) bool
-
+	// GetFormatterName 当前的 Formatter 是那个
+	GetFormatterName() string
+	// IsMatchThisFormat 是否满足当前实现接口的字幕命名格式 - 是否符合规则、subExt string, subLang types.Language, extraSubPreName string
+	IsMatchThisFormat(subName string) (bool, string, types.Language, string)
+	// GenerateMixSubName 通过视频和字幕信息,生成当前实现接口的字幕命名格式。extraSubPreName 一般是填写字幕网站,不填写则留空 - 新名称、新名称带有 default 标记,新名称带有 forced 标记
 	GenerateMixSubName(videoFileName, subExt string, subLang types.Language, extraSubPreName string) (string, string, string)
 }

+ 3 - 2
internal/pkg/hot_fix/hot_fix_001.go

@@ -5,6 +5,7 @@ import (
 	movieHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/movie_helper"
 	seriesHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/series_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/old"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
 	"os"
 )
@@ -59,7 +60,7 @@ func (h HotFix001) Process() (interface{}, error) {
 		}
 		// 判断是否是符合要求
 		for _, fitSubName := range fitMovieNameSubList {
-			bFix, _, newSubFileName := sub_helper.IsOldVersionSubPrefixName(fitSubName)
+			bFix, _, newSubFileName := old.IsOldVersionSubPrefixName(fitSubName)
 			if bFix == false {
 				continue
 			}
@@ -80,7 +81,7 @@ func (h HotFix001) Process() (interface{}, error) {
 		}
 		// 判断是否是符合要求
 		for _, fitSubName := range seriesSubFiles {
-			bFix, _, newSubFileName := sub_helper.IsOldVersionSubPrefixName(fitSubName)
+			bFix, _, newSubFileName := old.IsOldVersionSubPrefixName(fitSubName)
 			if bFix == false {
 				continue
 			}

+ 32 - 0
internal/pkg/language/language.go

@@ -164,6 +164,38 @@ func Lang2ChineseString(lan types.Language) string {
 	}
 }
 
+// ChineseString2Lang 将 中文描述:简、繁、简英 转换为 types.Language
+func ChineseString2Lang(chineseStr string) types.Language {
+	switch chineseStr {
+	case types.MathLangChnUnknow: // 未知语言
+		return types.Unknow
+	case types.MatchLangChs: // 简体中文
+		return types.ChineseSimple
+	case types.MatchLangCht: // 繁体中文
+		return types.ChineseTraditional
+	case types.MatchLangChsEn: // 简英双语字幕
+		return types.ChineseSimpleEnglish
+	case types.MatchLangChtEn: // 繁英双语字幕
+		return types.ChineseTraditionalEnglish
+	case types.MatchLangEn: // 英文
+		return types.English
+	case types.MatchLangJp: // 日语
+		return types.Japanese
+	case types.MatchLangChsJp: // 简日双语字幕
+		return types.ChineseSimpleJapanese
+	case types.MatchLangChtJp: // 繁日双语字幕
+		return types.ChineseTraditionalJapanese
+	case types.MatchLangKr: // 韩语
+		return types.Korean
+	case types.MatchLangChsKr: // 简韩双语字幕
+		return types.ChineseSimpleKorean
+	case types.MatchLangChtKr: // 繁韩双语字幕
+		return types.ChineseTraditionalKorean
+	default:
+		return types.Unknow
+	}
+}
+
 // GetLangOptions 语言识别的 Options Whitelist
 func GetLangOptions() whatlanggo.Options {
 	return whatlanggo.Options{

+ 80 - 0
internal/pkg/sub_formatter/emby/emby.go

@@ -0,0 +1,80 @@
+package emby
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/language"
+	"github.com/allanpk716/ChineseSubFinder/internal/types"
+	"path/filepath"
+	"regexp"
+	"strings"
+)
+
+type Formatter struct {
+}
+
+func NewFormatter() *Formatter {
+	return &Formatter{}
+}
+
+// GetFormatterName 当前的 Formatter 是那个
+func (f Formatter) GetFormatterName() string {
+	return "emby formatter"
+}
+
+// IsMatchThisFormat 是否满足当前实现接口的字幕命名格式 - 是否符合规则、subExt string, subLang types.Language, extraSubPreName string
+func (f Formatter) IsMatchThisFormat(subName string) (bool, string, types.Language, string) {
+	/*
+		Emby 的命名规则比较特殊,而且本程序就是做中文字幕下载的,所以,下面的正则表达式比较特殊
+	*/
+	var re = regexp.MustCompile(`(?m).chinese\((\S+)\)(\.\S+)`)
+	matched := re.FindAllStringSubmatch(subName, -1)
+	/*
+		[0][0]	.chinese(简英,subhd).ass
+		[0][1]	简英,subhd or 简英
+		[0][2]	.ass
+	*/
+	if len(matched) < 1 || len(matched[0]) < 3 {
+		return false, "", types.ChineseSimple, ""
+	}
+	var subLang types.Language
+	var subLangStr string
+	var extraSubPreName string
+	subExt := matched[0][2]
+	midString := matched[0][1]
+	if strings.Contains(midString, ",") == true {
+		tmps := strings.Split(midString, ",")
+		if len(tmps) < 2 {
+			return false, "", types.ChineseSimple, ""
+		}
+		subLangStr = tmps[0]
+		extraSubPreName = tmps[1]
+	} else {
+		subLangStr = midString
+		extraSubPreName = ""
+	}
+	subLang = language.ChineseString2Lang(subLangStr)
+
+	return true, subExt, subLang, extraSubPreName
+}
+
+// GenerateMixSubName 通过视频和字幕信息,生成当前实现接口的字幕命名格式。extraSubPreName 一般是填写字幕网站,不填写则留空 - 新名称、新名称带有 default 标记,新名称带有 forced 标记
+func (f Formatter) GenerateMixSubName(videoFileName, subExt string, subLang types.Language, extraSubPreName string) (string, string, string) {
+	/*
+		这里会生成类似的文件名 xxxx.chinese(中英,shooter)
+	*/
+	videoFileNameWithOutExt := strings.ReplaceAll(filepath.Base(videoFileName),
+		filepath.Ext(videoFileName), "")
+	note := ""
+	// extraSubPreName 那个字幕网站下载的
+	if extraSubPreName != "" {
+		note = "," + extraSubPreName
+	}
+	const defaultString = ".default"
+	const forcedString = ".forced"
+	const chineseString = ".chinese"
+
+	subNewName := videoFileNameWithOutExt + chineseString + "(" + language.Lang2ChineseString(subLang) + note + ")" + subExt
+	subNewNameWithDefault := videoFileNameWithOutExt + chineseString + "(" + language.Lang2ChineseString(subLang) + note + ")" + defaultString + subExt
+	subNewNameWithForced := videoFileNameWithOutExt + chineseString + "(" + language.Lang2ChineseString(subLang) + note + ")" + forcedString + subExt
+
+	return subNewName, subNewNameWithDefault, subNewNameWithForced
+}

+ 130 - 0
internal/pkg/sub_formatter/emby/emby_test.go

@@ -0,0 +1,130 @@
+package emby
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/common"
+	"github.com/allanpk716/ChineseSubFinder/internal/types"
+	"testing"
+)
+
+func TestFormatter_GetFormatterName(t *testing.T) {
+	f := NewFormatter()
+	if f.GetFormatterName() != "emby formatter" {
+		t.Errorf("GetFormatterName error")
+	}
+}
+
+func TestFormatter_IsMatchThisFormat(t *testing.T) {
+	type args struct {
+		subName string
+	}
+	tests := []struct {
+		name  string
+		args  args
+		want  bool
+		want1 string
+		want2 types.Language
+		want3 string
+	}{
+		{name: "00", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(简英,subhd).ass"},
+			want:  true,
+			want1: ".ass",
+			want2: types.ChineseSimpleEnglish,
+			want3: "subhd"},
+		{name: "01", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(简英,xunlei).default.ass"},
+			want:  true,
+			want1: ".default.ass",
+			want2: types.ChineseSimpleEnglish,
+			want3: "xunlei"},
+		{name: "02", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(简英,zimuku).forced.ass"},
+			want:  true,
+			want1: ".forced.ass",
+			want2: types.ChineseSimpleEnglish,
+			want3: "zimuku"},
+		{name: "10", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(简日).ass"},
+			want:  true,
+			want1: ".ass",
+			want2: types.ChineseSimpleJapanese,
+			want3: ""},
+		{name: "11", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(简).default.ass"},
+			want:  true,
+			want1: ".default.ass",
+			want2: types.ChineseSimple,
+			want3: ""},
+		{name: "12", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese(繁英).forced.ass"},
+			want:  true,
+			want1: ".forced.ass",
+			want2: types.ChineseTraditionalEnglish,
+			want3: ""},
+		{name: "03", args: args{subName: "The Boss Baby Family Business (2021) WEBDL-1080p.chinese.ass"},
+			want:  false,
+			want1: "",
+			want2: types.ChineseSimple,
+			want3: ""},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			f := Formatter{}
+			got, got1, got2, got3 := f.IsMatchThisFormat(tt.args.subName)
+			if got != tt.want {
+				t.Errorf("IsMatchThisFormat() got = %v, want %v", got, tt.want)
+			}
+			if got1 != tt.want1 {
+				t.Errorf("IsMatchThisFormat() got1 = %v, want %v", got1, tt.want1)
+			}
+			if got2 != tt.want2 {
+				t.Errorf("IsMatchThisFormat() got2 = %v, want %v", got2, tt.want2)
+			}
+			if got3 != tt.want3 {
+				t.Errorf("IsMatchThisFormat() got3 = %v, want %v", got3, tt.want3)
+			}
+		})
+	}
+}
+
+func TestFormatter_GenerateMixSubName(t *testing.T) {
+
+	const videoFileName = "Django Unchained (2012) Bluray-1080p.mp4"
+	const videoFileNamePre = "Django Unchained (2012) Bluray-1080p"
+
+	type args struct {
+		videoFileName   string
+		subExt          string
+		subLang         types.Language
+		extraSubPreName string
+	}
+	tests := []struct {
+		name  string
+		args  args
+		want  string
+		want1 string
+		want2 string
+	}{
+		{name: "zh_shooter", args: args{videoFileName: videoFileName, subExt: common.SubExtASS, subLang: types.ChineseSimple, extraSubPreName: "shooter"},
+			want:  videoFileNamePre + ".chinese(简,shooter).ass",
+			want1: videoFileNamePre + ".chinese(简,shooter).default.ass",
+			want2: videoFileNamePre + ".chinese(简,shooter).forced.ass"},
+		{name: "zh_en_shooter", args: args{videoFileName: videoFileName, subExt: common.SubExtASS, subLang: types.ChineseSimpleEnglish, extraSubPreName: "shooter"},
+			want:  videoFileNamePre + ".chinese(简英,shooter).ass",
+			want1: videoFileNamePre + ".chinese(简英,shooter).default.ass",
+			want2: videoFileNamePre + ".chinese(简英,shooter).forced.ass"},
+		{name: "zh_en", args: args{videoFileName: videoFileName, subExt: common.SubExtASS, subLang: types.ChineseSimpleEnglish, extraSubPreName: ""},
+			want:  videoFileNamePre + ".chinese(简英).ass",
+			want1: videoFileNamePre + ".chinese(简英).default.ass",
+			want2: videoFileNamePre + ".chinese(简英).forced.ass"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			f := Formatter{}
+			got, got1, got2 := f.GenerateMixSubName(tt.args.videoFileName, tt.args.subExt, tt.args.subLang, tt.args.extraSubPreName)
+			if got != tt.want {
+				t.Errorf("GenerateMixSubName() got = %v, want %v", got, tt.want)
+			}
+			if got1 != tt.want1 {
+				t.Errorf("GenerateMixSubName() got1 = %v, want %v", got1, tt.want1)
+			}
+			if got2 != tt.want2 {
+				t.Errorf("GenerateMixSubName() got2 = %v, want %v", got2, tt.want2)
+			}
+		})
+	}
+}

+ 1 - 0
internal/pkg/sub_formatter/normal/normal.go

@@ -0,0 +1 @@
+package normal

+ 120 - 0
internal/pkg/sub_formatter/old/old.go

@@ -0,0 +1,120 @@
+package old
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/common"
+	"github.com/allanpk716/ChineseSubFinder/internal/types"
+	"path/filepath"
+	"strings"
+)
+
+// IsOldVersionSubPrefixName 是否是老版本的字幕命名 .chs_en[shooter] ,符合也返回这个部分+字幕格式后缀名 .chs_en[shooter].ass, 修改后的名称
+func IsOldVersionSubPrefixName(subFileName string) (bool, string, string) {
+	/*
+		{
+			name: "chs_en[shooter]", args: args{
+			subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en[shooter].ass"},
+			want: true,
+			want1: ".chs_en[shooter].ass",
+			want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简英,shooter).ass"
+		},
+			传入的必须是字幕格式的文件,这个就再之前判断,不要在这里再判断
+			传入的文件名可能有一下几种情况
+			无罪之最 - S01E01 - 重建生活.chs[shooter].ass
+			无罪之最 - S01E03 - 初见端倪.zh.srt
+			Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en.ass
+			那么就需要先剔除,字幕的格式后缀名,然后再向后取后缀名就是 .chs[shooter] or .zh
+			再判断即可
+	*/
+	// 无罪之最 - S01E01 - 重建生活.chs[shooter].ass -> 无罪之最 - S01E01 - 重建生活.chs[shooter]
+	subTypeExt := filepath.Ext(subFileName)
+	subFileNameWithOutExt := strings.ReplaceAll(subFileName, subTypeExt, "")
+	// .chs[shooter]
+	nowExt := filepath.Ext(subFileNameWithOutExt)
+	// .chs_en[shooter].ass
+	orgMixExt := nowExt + subTypeExt
+	orgFileNameWithOutOrgMixExt := strings.ReplaceAll(subFileName, orgMixExt, "")
+	// 这里也有两种情况,一种是单字幕 SaveMultiSub: false
+	// 一种的保存了多字幕 SaveMultiSub: true
+	// 先判断 单字幕
+	switch nowExt {
+	case types.Emby_chs:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChs, subTypeExt, "", true)
+	case types.Emby_cht:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangCht, subTypeExt, "", false)
+	case types.Emby_chs_en:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsEn, subTypeExt, "", true)
+	case types.Emby_cht_en:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtEn, subTypeExt, "", false)
+	case types.Emby_chs_jp:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsJp, subTypeExt, "", true)
+	case types.Emby_cht_jp:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtJp, subTypeExt, "", false)
+	case types.Emby_chs_kr:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsKr, subTypeExt, "", true)
+	case types.Emby_cht_kr:
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtKr, subTypeExt, "", false)
+	}
+	// 再判断 多字幕情况
+	spStrings := strings.Split(nowExt, "[")
+	if len(spStrings) != 2 {
+		return false, "", ""
+	}
+	// 分两段来判断是否符合标准
+	// 第一段
+	firstOk := true
+	lang := types.MatchLangChs
+	site := ""
+	switch spStrings[0] {
+	case types.Emby_chs:
+		lang = types.MatchLangChs
+	case types.Emby_cht:
+		lang = types.MatchLangCht
+	case types.Emby_chs_en:
+		lang = types.MatchLangChsEn
+	case types.Emby_cht_en:
+		lang = types.MatchLangChtEn
+	case types.Emby_chs_jp:
+		lang = types.MatchLangChsJp
+	case types.Emby_cht_jp:
+		lang = types.MatchLangChtJp
+	case types.Emby_chs_kr:
+		lang = types.MatchLangChsKr
+	case types.Emby_cht_kr:
+		lang = types.MatchLangChtKr
+	default:
+		firstOk = false
+	}
+	// 第二段
+	secondOk := true
+	tmpSecond := strings.ReplaceAll(spStrings[1], "]", "")
+	switch tmpSecond {
+	case common.SubSiteZiMuKu:
+		site = common.SubSiteZiMuKu
+	case common.SubSiteSubHd:
+		site = common.SubSiteSubHd
+	case common.SubSiteShooter:
+		site = common.SubSiteShooter
+	case common.SubSiteXunLei:
+		site = common.SubSiteXunLei
+	default:
+		secondOk = false
+	}
+	// 都要符合条件
+	if firstOk == true && secondOk == true {
+		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, lang, subTypeExt, site, false)
+	}
+	return false, "", ""
+}
+
+func makeMixSubExtString(orgFileNameWithOutExt, lang string, ext, site string, beDefault bool) string {
+
+	tmpDefault := ""
+	if beDefault == true {
+		tmpDefault = types.Emby_default
+	}
+
+	if site == "" {
+		return orgFileNameWithOutExt + types.Emby_chinese + "(" + lang + ")" + tmpDefault + ext
+	}
+	return orgFileNameWithOutExt + types.Emby_chinese + "(" + lang + "," + site + ")" + tmpDefault + ext
+}

+ 36 - 0
internal/pkg/sub_formatter/old/old_test.go

@@ -0,0 +1,36 @@
+package old
+
+import "testing"
+
+func TestIsOldVersionSubPrefixName(t *testing.T) {
+	type args struct {
+		subFileName string
+	}
+	tests := []struct {
+		name  string
+		args  args
+		want  bool
+		want1 string
+		want2 string
+	}{
+		{name: "chs_en", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en.ass"}, want: true, want1: ".chs_en.ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简英).default.ass"},
+		{name: "chs[subhd]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs[subhd].ass"}, want: true, want1: ".chs[subhd].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简,subhd).ass"},
+		{name: "chs_en[shooter]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en[shooter].ass"}, want: true, want1: ".chs_en[shooter].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简英,shooter).ass"},
+		{name: "cht_en[xunlei]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.cht_en[xunlei].ass"}, want: true, want1: ".cht_en[xunlei].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(繁英,xunlei).ass"},
+		{name: "zh", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.zh.ass"}, want: false, want1: "", want2: ""},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, got1, got2 := IsOldVersionSubPrefixName(tt.args.subFileName)
+			if got != tt.want {
+				t.Errorf("IsOldVersionSubPrefixName() got = %v, want %v", got, tt.want)
+			}
+			if got1 != tt.want1 {
+				t.Errorf("IsOldVersionSubPrefixName() got1 = %v, want %v", got1, tt.want1)
+			}
+			if got2 != tt.want2 {
+				t.Errorf("IsOldVersionSubPrefixName() got2 = %v, want %v", got2, tt.want2)
+			}
+		})
+	}
+}

+ 18 - 135
internal/pkg/sub_helper/sub_helper.go

@@ -230,8 +230,8 @@ func SearchMatchedSubFile(dir string) ([]string, error) {
 	return fileFullPathList, nil
 }
 
-// SearchVideoMatchSubFileAndRemoveDefaultMark 找到找个视频目录下相匹配的字幕,同时去除这些字幕中 .default 的标记
-func SearchVideoMatchSubFileAndRemoveDefaultMark(oneVideoFullPath string) error {
+// SearchVideoMatchSubFileAndRemoveExtMark 找到找个视频目录下相匹配的字幕,同时去除这些字幕中 .default 或者 .forced 的标记。注意这两个标记不应该同时出现,否则无法正确去除
+func SearchVideoMatchSubFileAndRemoveExtMark(oneVideoFullPath string) error {
 
 	dir := filepath.Dir(oneVideoFullPath)
 	fileName := filepath.Base(oneVideoFullPath)
@@ -261,147 +261,30 @@ func SearchVideoMatchSubFileAndRemoveDefaultMark(oneVideoFullPath string) error
 				continue
 			}
 			// 得包含 .default. 找个关键词
-			if strings.Contains(nowFileName, types.Emby_default+".") == false {
+			if strings.Contains(nowFileName, types.Sub_Ext_Mark_Default+".") == true {
+				oldPath := dir + pathSep + curFile.Name()
+				newPath := dir + pathSep + strings.ReplaceAll(curFile.Name(), types.Sub_Ext_Mark_Default+".", ".")
+				err = os.Rename(oldPath, newPath)
+				if err != nil {
+					return err
+				}
+			} else if strings.Contains(nowFileName, types.Sub_Ext_Mark_Forced+".") == true {
+				// 得包含 .forced. 找个关键词
+				oldPath := dir + pathSep + curFile.Name()
+				newPath := dir + pathSep + strings.ReplaceAll(curFile.Name(), types.Sub_Ext_Mark_Forced+".", ".")
+				err = os.Rename(oldPath, newPath)
+				if err != nil {
+					return err
+				}
+			} else {
 				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) {
-
-	/*
-		传入的必须是字幕格式的文件,这个就再之前判断,不要在这里再判断
-		传入的文件名可能有一下几种情况
-		无罪之最 - S01E01 - 重建生活.chs[shooter].ass
-		无罪之最 - S01E03 - 初见端倪.zh.srt
-		Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en.ass
-		那么就需要先剔除,字幕的格式后缀名,然后再向后取后缀名就是 .chs[shooter] or .zh
-		再判断即可
-	*/
-	// 无罪之最 - S01E01 - 重建生活.chs[shooter].ass -> 无罪之最 - S01E01 - 重建生活.chs[shooter]
-	subTypeExt := filepath.Ext(subFileName)
-	subFileNameWithOutExt := strings.ReplaceAll(subFileName, subTypeExt, "")
-	// .chs[shooter]
-	nowExt := filepath.Ext(subFileNameWithOutExt)
-	// .chs_en[shooter].ass
-	orgMixExt := nowExt + subTypeExt
-	orgFileNameWithOutOrgMixExt := strings.ReplaceAll(subFileName, orgMixExt, "")
-	// 这里也有两种情况,一种是单字幕 SaveMultiSub: false
-	// 一种的保存了多字幕 SaveMultiSub: true
-	// 先判断 单字幕
-	switch nowExt {
-	case types.Emby_chs:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChs, subTypeExt, "", true)
-	case types.Emby_cht:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangCht, subTypeExt, "", false)
-	case types.Emby_chs_en:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsEn, subTypeExt, "", true)
-	case types.Emby_cht_en:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtEn, subTypeExt, "", false)
-	case types.Emby_chs_jp:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsJp, subTypeExt, "", true)
-	case types.Emby_cht_jp:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtJp, subTypeExt, "", false)
-	case types.Emby_chs_kr:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChsKr, subTypeExt, "", true)
-	case types.Emby_cht_kr:
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, types.MatchLangChtKr, subTypeExt, "", false)
-	}
-	// 再判断 多字幕情况
-	spStrings := strings.Split(nowExt, "[")
-	if len(spStrings) != 2 {
-		return false, "", ""
-	}
-	// 分两段来判断是否符合标准
-	// 第一段
-	firstOk := true
-	lang := types.MatchLangChs
-	site := ""
-	switch spStrings[0] {
-	case types.Emby_chs:
-		lang = types.MatchLangChs
-	case types.Emby_cht:
-		lang = types.MatchLangCht
-	case types.Emby_chs_en:
-		lang = types.MatchLangChsEn
-	case types.Emby_cht_en:
-		lang = types.MatchLangChtEn
-	case types.Emby_chs_jp:
-		lang = types.MatchLangChsJp
-	case types.Emby_cht_jp:
-		lang = types.MatchLangChtJp
-	case types.Emby_chs_kr:
-		lang = types.MatchLangChsKr
-	case types.Emby_cht_kr:
-		lang = types.MatchLangChtKr
-	default:
-		firstOk = false
-	}
-	// 第二段
-	secondOk := true
-	tmpSecond := strings.ReplaceAll(spStrings[1], "]", "")
-	switch tmpSecond {
-	case common.SubSiteZiMuKu:
-		site = common.SubSiteZiMuKu
-	case common.SubSiteSubHd:
-		site = common.SubSiteSubHd
-	case common.SubSiteShooter:
-		site = common.SubSiteShooter
-	case common.SubSiteXunLei:
-		site = common.SubSiteXunLei
-	default:
-		secondOk = false
-	}
-	// 都要符合条件
-	if firstOk == true && secondOk == true {
-		return true, orgMixExt, makeMixSubExtString(orgFileNameWithOutOrgMixExt, lang, subTypeExt, site, false)
-	}
-	return false, "", ""
-}
-
-// GenerateMixSubName 这里会生成类似的文件名 xxxx.chinese(中英,shooter)
-func GenerateMixSubName(videoFileName, subExt string, subLang types.Language, extraSubPreName string) (string, string, string) {
-
-	videoFileNameWithOutExt := strings.ReplaceAll(filepath.Base(videoFileName),
-		filepath.Ext(videoFileName), "")
-	note := ""
-	// extraSubPreName 那个字幕网站下载的
-	if extraSubPreName != "" {
-		note = "," + extraSubPreName
-	}
-	defaultString := ".default"
-	forcedString := ".forced"
-
-	subNewName := videoFileNameWithOutExt + ".chinese" + "(" + language.Lang2ChineseString(subLang) + note + ")" + subExt
-	subNewNameWithDefault := videoFileNameWithOutExt + ".chinese" + "(" + language.Lang2ChineseString(subLang) + note + ")" + defaultString + subExt
-	subNewNameWithForced := videoFileNameWithOutExt + ".chinese" + "(" + language.Lang2ChineseString(subLang) + note + ")" + forcedString + subExt
-
-	return subNewName, subNewNameWithDefault, subNewNameWithForced
-}
-
-func makeMixSubExtString(orgFileNameWithOutExt, lang string, ext, site string, beDefault bool) string {
-
-	tmpDefault := ""
-	if beDefault == true {
-		tmpDefault = types.Emby_default
-	}
-
-	if site == "" {
-		return orgFileNameWithOutExt + types.Emby_chinese + "(" + lang + ")" + tmpDefault + ext
-	}
-	return orgFileNameWithOutExt + types.Emby_chinese + "(" + lang + "," + site + ")" + tmpDefault + ext
-}
-
 // DeleteOneSeasonSubCacheFolder 删除一个连续剧中的所有一季字幕的缓存文件夹
 func DeleteOneSeasonSubCacheFolder(seriesDir string) error {
 

+ 0 - 33
internal/pkg/sub_helper/sub_helper_test.go

@@ -6,39 +6,6 @@ import (
 	"testing"
 )
 
-func TestIsOldVersionSubPrefixName(t *testing.T) {
-	type args struct {
-		subFileName string
-	}
-	tests := []struct {
-		name  string
-		args  args
-		want  bool
-		want1 string
-		want2 string
-	}{
-		{name: "chs_en", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en.ass"}, want: true, want1: ".chs_en.ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简英).default.ass"},
-		{name: "chs[subhd]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs[subhd].ass"}, want: true, want1: ".chs[subhd].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简,subhd).ass"},
-		{name: "chs_en[shooter]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chs_en[shooter].ass"}, want: true, want1: ".chs_en[shooter].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(简英,shooter).ass"},
-		{name: "cht_en[xunlei]", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.cht_en[xunlei].ass"}, want: true, want1: ".cht_en[xunlei].ass", want2: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.chinese(繁英,xunlei).ass"},
-		{name: "zh", args: args{subFileName: "Loki - S01E01 - Glorious Purpose WEBDL-1080p Proper.zh.ass"}, want: false, want1: "", want2: ""},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, got1, got2 := IsOldVersionSubPrefixName(tt.args.subFileName)
-			if got != tt.want {
-				t.Errorf("IsOldVersionSubPrefixName() got = %v, want %v", got, tt.want)
-			}
-			if got1 != tt.want1 {
-				t.Errorf("IsOldVersionSubPrefixName() got1 = %v, want %v", got1, tt.want1)
-			}
-			if got2 != tt.want2 {
-				t.Errorf("IsOldVersionSubPrefixName() got2 = %v, want %v", got2, tt.want2)
-			}
-		})
-	}
-}
-
 func TestDeleteOneSeasonSubCacheFolder(t *testing.T) {
 
 	testDataPath := "..\\..\\..\\TestData\\sub_helper"

+ 5 - 0
internal/types/language.go

@@ -23,6 +23,11 @@ const (
 	ChineseTraditionalKorean                   // 繁韩双语字幕
 )
 
+const (
+	Sub_Ext_Mark_Default = ".default" // 指定这个字幕是默认的
+	Sub_Ext_Mark_Forced  = ".forced"  // 指定这个字幕是强制的
+)
+
 // 需要符合 emby_helper 的格式要求,在后缀名前面
 const (
 	Emby_default = ".default" // 指定这个字幕是默认的