sub_format_changer.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package sub_formatter
  2. import (
  3. "errors"
  4. "fmt"
  5. interCommon "github.com/allanpk716/ChineseSubFinder/internal/common"
  6. "github.com/allanpk716/ChineseSubFinder/internal/dao"
  7. "github.com/allanpk716/ChineseSubFinder/internal/ifaces"
  8. movieHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/movie_helper"
  9. seriesHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/series_helper"
  10. "github.com/allanpk716/ChineseSubFinder/internal/models"
  11. "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
  12. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  13. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/common"
  14. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/emby"
  15. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/normal"
  16. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
  17. "github.com/allanpk716/ChineseSubFinder/internal/types/subparser"
  18. "gorm.io/gorm"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. )
  23. type SubFormatChanger struct {
  24. movieRootDirs []string
  25. seriesRootDirs []string
  26. formatter map[string]ifaces.ISubFormatter
  27. }
  28. func NewSubFormatChanger(movieRootDirs []string, seriesRootDirs []string) *SubFormatChanger {
  29. formatter := SubFormatChanger{movieRootDirs: movieRootDirs, seriesRootDirs: seriesRootDirs}
  30. // TODO 如果字幕格式新增了实现,这里也需要添加对应的实例
  31. // 初始化支持的 formatter
  32. // normal
  33. formatter.formatter = make(map[string]ifaces.ISubFormatter)
  34. normalM := normal.NewFormatter()
  35. formatter.formatter[normalM.GetFormatterName()] = normalM
  36. // emby
  37. embyM := emby.NewFormatter()
  38. formatter.formatter[embyM.GetFormatterName()] = embyM
  39. return &formatter
  40. }
  41. // AutoDetectThenChangeTo 自动检测字幕的命名格式,然后转换到目标的 formatter 上
  42. func (s SubFormatChanger) AutoDetectThenChangeTo(desFormatter common.FormatterName) (RenameResults, error) {
  43. outStruct := RenameResults{}
  44. outStruct.RenamedFiles = make(map[string]int)
  45. outStruct.ErrFiles = make(map[string]int)
  46. for i, dir := range s.movieRootDirs {
  47. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Movie Index", i, dir, "Start")
  48. err := s.autoDetectMovieThenChangeTo(&outStruct, desFormatter, dir)
  49. if err != nil {
  50. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Movie Index", i, dir, "End")
  51. return RenameResults{}, err
  52. }
  53. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Movie Index", i, dir, "Start")
  54. }
  55. for i, dir := range s.seriesRootDirs {
  56. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Series Index", i, dir, "Start")
  57. err := s.autoDetectMovieThenChangeTo(&outStruct, desFormatter, dir)
  58. if err != nil {
  59. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Series Index", i, dir, "End")
  60. return RenameResults{}, err
  61. }
  62. log_helper.GetLogger().Infoln("AutoDetectThenChangeTo Series Index", i, dir, "Start")
  63. }
  64. return outStruct, nil
  65. }
  66. func (s SubFormatChanger) autoDetectMovieThenChangeTo(outStruct *RenameResults, desFormatter common.FormatterName, movieRootDir string) error {
  67. var err error
  68. if my_util.IsDir(movieRootDir) == false {
  69. return errors.New("movieRootDir path not exist: " + movieRootDir)
  70. }
  71. // 先找出有那些电影文件夹和连续剧文件夹
  72. var movieFullPathList = make([]string, 0)
  73. movieFullPathList, err = my_util.SearchMatchedVideoFile(log_helper.GetLogger(), movieRootDir)
  74. // fmt.Println("No. of Movies: ", len(movieFullPathList), " dir: ", s.movieRootDir)
  75. if err != nil {
  76. return err
  77. }
  78. // 搜索所有的字幕,找到相关的字幕进行修改
  79. for _, one := range movieFullPathList {
  80. // 需要判断这个视频根目录是否有 .ignore 文件,有也跳过
  81. if my_util.IsFile(filepath.Join(filepath.Dir(one), interCommon.Ignore)) == true {
  82. log_helper.GetLogger().Infoln("Found", interCommon.Ignore, "Skip", one)
  83. // 跳过下载字幕
  84. continue
  85. }
  86. found := false
  87. var fitMovieNameSubList = make([]string, 0)
  88. found, _, fitMovieNameSubList, err = movieHelper.MovieHasChineseSub(one)
  89. if err != nil || found == false {
  90. continue
  91. }
  92. // 判断是否是符合要求
  93. for _, fitSubName := range fitMovieNameSubList {
  94. s.autoDetectAndChange(outStruct, fitSubName, desFormatter)
  95. }
  96. }
  97. return nil
  98. }
  99. func (s SubFormatChanger) autoDetectMSeriesThenChangeTo(outStruct *RenameResults, desFormatter common.FormatterName, seriesRootDir string) error {
  100. var err error
  101. if my_util.IsDir(seriesRootDir) == false {
  102. return errors.New("seriesRootDir path not exist: " + seriesRootDir)
  103. }
  104. // 先找出有那些电影文件夹和连续剧文件夹
  105. seriesDirList, err := seriesHelper.GetSeriesList(seriesRootDir)
  106. if err != nil {
  107. return err
  108. }
  109. // 连续剧
  110. var seriesSubFiles = make([]string, 0)
  111. for _, oneSeriesDir := range seriesDirList {
  112. // 需要判断这个视频根目录是否有 .ignore 文件,有也跳过
  113. if my_util.IsFile(filepath.Join(oneSeriesDir, interCommon.Ignore)) == true {
  114. log_helper.GetLogger().Infoln("Found", interCommon.Ignore, "Skip", oneSeriesDir)
  115. // 跳过下载字幕
  116. continue
  117. }
  118. seriesSubFiles, err = sub_helper.SearchMatchedSubFileByDir(oneSeriesDir)
  119. if err != nil {
  120. return err
  121. }
  122. // 判断是否是符合要求
  123. for _, fitSubName := range seriesSubFiles {
  124. s.autoDetectAndChange(outStruct, fitSubName, desFormatter)
  125. }
  126. }
  127. return nil
  128. }
  129. // autoDetectAndChange 自动检测命名格式,然后修改至目标的命名格式
  130. func (s SubFormatChanger) autoDetectAndChange(outStruct *RenameResults, fitSubName string, desFormatter common.FormatterName) {
  131. for _, formatter := range s.formatter {
  132. // true, , ./../../TestData/sub_format_changer/test/movie_org_emby/AAA/AAA.chinese(简英,subhd).ass, 未知语言, ,
  133. bok, fileNameWithOutExt, subExt, subLang, extraSubPreName := formatter.IsMatchThisFormat(fitSubName)
  134. if bok == false {
  135. continue
  136. }
  137. // 如果检测到的格式和目标要转换到的格式是一个,那么就跳过
  138. if common.FormatterName(formatter.GetFormatterFormatterName()) == desFormatter {
  139. return
  140. }
  141. // 这里得到的 subExt 可能是 .ass or .default.ass or .forced.ass
  142. // 需要进行剔除,因为后续的 GenerateMixSubName 会自动生成对应的附加后缀名
  143. // 转换格式后,需要保留之前的 default 或者 forced
  144. findDefault := false
  145. findForce := false
  146. if strings.Contains(subExt, subparser.Sub_Ext_Mark_Default) == true {
  147. subExt = strings.Replace(subExt, subparser.Sub_Ext_Mark_Default, "", -1)
  148. findDefault = true
  149. }
  150. if strings.Contains(subExt, subparser.Sub_Ext_Mark_Forced) == true {
  151. subExt = strings.Replace(subExt, subparser.Sub_Ext_Mark_Forced, "", -1)
  152. findForce = true
  153. }
  154. // 通过传入的目标格式化 formatter 的名称去调用
  155. newSubFileName := ""
  156. newName, newDefaultName, newForcedName := s.formatter[fmt.Sprintf("%s", desFormatter)].
  157. GenerateMixSubNameBase(fileNameWithOutExt, subExt, subLang, extraSubPreName)
  158. // fmt.Println(fmt.Sprintf("%s", desFormatter))
  159. // fmt.Println("newName : " + newName)
  160. // fmt.Println("newDefaultName: " + newDefaultName)
  161. // fmt.Println("newForcedName : " + newForcedName)
  162. if findDefault == false && findForce == false {
  163. // 使用没得额外 Default 或者 Forced 的名称即可
  164. newSubFileName = newName
  165. } else if findDefault == true {
  166. newSubFileName = newDefaultName
  167. } else if findForce == true {
  168. newSubFileName = newForcedName
  169. }
  170. if newSubFileName == "" {
  171. continue
  172. }
  173. // 确认改格式
  174. err := os.Rename(fitSubName, newSubFileName)
  175. if err != nil {
  176. tmpName := my_util.FixWindowPathBackSlash(fitSubName)
  177. outStruct.ErrFiles[tmpName] += 1
  178. continue
  179. } else {
  180. tmpName := my_util.FixWindowPathBackSlash(newSubFileName)
  181. outStruct.RenamedFiles[tmpName] += 1
  182. }
  183. }
  184. }
  185. type RenameResults struct {
  186. RenamedFiles map[string]int
  187. ErrFiles map[string]int
  188. }
  189. // GetSubFormatter 选择字幕命名格式化的实例
  190. func GetSubFormatter(subNameFormatter int) ifaces.ISubFormatter {
  191. var subFormatter ifaces.ISubFormatter
  192. switch subNameFormatter {
  193. case int(common.Emby):
  194. {
  195. subFormatter = emby.NewFormatter()
  196. break
  197. }
  198. case int(common.Normal):
  199. {
  200. subFormatter = normal.NewFormatter()
  201. break
  202. }
  203. default:
  204. {
  205. subFormatter = emby.NewFormatter()
  206. break
  207. }
  208. }
  209. return subFormatter
  210. }
  211. // SubFormatChangerProcess 执行 SubFormatChanger 逻辑,并且更新数据库缓存
  212. func SubFormatChangerProcess(movieRootDirs []string, seriesRootDirs []string, nowDesFormatter common.FormatterName) (RenameResults, error) {
  213. var subFormatRec models.SubFormatRec
  214. re := dao.GetDb().First(&subFormatRec)
  215. if re == nil {
  216. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().First return nil"))
  217. }
  218. if re.Error != nil {
  219. if re.Error != gorm.ErrRecordNotFound {
  220. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().First, %v", re.Error))
  221. }
  222. }
  223. subFormatChanger := NewSubFormatChanger(movieRootDirs, seriesRootDirs)
  224. // 理论上有且仅有一条记录
  225. if subFormatRec.Done == false {
  226. // 没有找到,认为是第一次执行
  227. renameResults, err := subFormatChanger.AutoDetectThenChangeTo(nowDesFormatter)
  228. if err != nil {
  229. return renameResults, err
  230. }
  231. // 需要记录到数据库中
  232. oneSubFormatter := models.SubFormatRec{FormatName: int(nowDesFormatter), Done: true}
  233. re = dao.GetDb().Create(&oneSubFormatter)
  234. if re == nil {
  235. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().Create return nil"))
  236. }
  237. if re.Error != nil {
  238. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().Create, %v", re.Error))
  239. }
  240. return renameResults, nil
  241. } else {
  242. // 找到了,需要判断上一次执行的目标 formatter 是啥,如果这次的目标 formatter 不一样则执行
  243. // 如果是一样的则跳过
  244. if common.FormatterName(subFormatRec.FormatName) == nowDesFormatter {
  245. log_helper.GetLogger().Infoln("DesSubFormatter == LateTimeSubFormatter then skip process")
  246. return RenameResults{}, nil
  247. }
  248. // 执行更改
  249. renameResults, err := subFormatChanger.AutoDetectThenChangeTo(nowDesFormatter)
  250. if err != nil {
  251. return renameResults, err
  252. }
  253. // 更新数据库
  254. subFormatRec.FormatName = int(nowDesFormatter)
  255. subFormatRec.Done = true
  256. re = dao.GetDb().Save(subFormatRec)
  257. if re == nil {
  258. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().Save return nil"))
  259. }
  260. if re.Error != nil {
  261. return RenameResults{}, errors.New(fmt.Sprintf("SubFormatChangerProcess dao.GetDb().Save, %v", re.Error))
  262. }
  263. return renameResults, nil
  264. }
  265. }