sub_timeline_fixer_helper.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package sub_timeline_fixer
  2. //
  3. //import (
  4. // "fmt"
  5. // "github.com/allanpk716/ChineseSubFinder/internal/common"
  6. // "github.com/allanpk716/ChineseSubFinder/internal/ifaces"
  7. // "github.com/allanpk716/ChineseSubFinder/internal/logic/emby_helper"
  8. // "github.com/allanpk716/ChineseSubFinder/internal/logic/sub_parser/ass"
  9. // "github.com/allanpk716/ChineseSubFinder/internal/logic/sub_parser/srt"
  10. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
  11. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  12. // formatterEmby "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/emby"
  13. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/normal"
  14. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
  15. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_parser_hub"
  16. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_timeline_fixer"
  17. // "github.com/allanpk716/ChineseSubFinder/internal/pkg/vad"
  18. // "github.com/allanpk716/ChineseSubFinder/internal/types/emby"
  19. // "github.com/allanpk716/ChineseSubFinder/internal/types/sub_timeline_fiexer"
  20. // "github.com/allanpk716/ChineseSubFinder/internal/types/subparser"
  21. // "os"
  22. // "path"
  23. // "path/filepath"
  24. // "strings"
  25. // "time"
  26. //)
  27. //
  28. //type SubTimelineFixerHelper struct {
  29. // log *logrus.Logger
  30. // embyHelper *emby_helper.EmbyHelper
  31. // EmbyConfig emby.EmbyConfig
  32. // FixerConfig sub_timeline_fiexer.SubTimelineFixerConfig
  33. // subParserHub *sub_parser_hub.SubParserHub
  34. // subTimelineFixer *sub_timeline_fixer.SubTimelineFixer
  35. // formatter map[string]ifaces.ISubFormatter
  36. // threads int
  37. // timeOut time.Duration
  38. //}
  39. //
  40. //func NewSubTimelineFixerHelper(log *logrus.Logger, embyConfig emby.EmbyConfig, subTimelineFixerConfig sub_timeline_fiexer.SubTimelineFixerConfig) *SubTimelineFixerHelper {
  41. // sub := SubTimelineFixerHelper{
  42. //
  43. // EmbyConfig: embyConfig,
  44. // FixerConfig: subTimelineFixerConfig,
  45. // embyHelper: emby_helper.NewEmbyHelper(embyConfig),
  46. // subParserHub: sub_parser_hub.NewSubParserHub(ass.NewParser(), srt.NewParser()),
  47. // subTimelineFixer: sub_timeline_fixer.NewSubTimelineFixer(subTimelineFixerConfig),
  48. // formatter: make(map[string]ifaces.ISubFormatter),
  49. // threads: 6,
  50. // timeOut: 60 * time.Second,
  51. // }
  52. // // TODO 如果字幕格式新增了实现,这里也需要添加对应的实例
  53. // // 初始化支持的 formatter
  54. // // normal
  55. // sub.formatter = make(map[string]ifaces.ISubFormatter)
  56. // normalM := normal.NewFormatter()
  57. // sub.formatter[normalM.GetFormatterName()] = normalM
  58. // // emby
  59. // embyM := formatterEmby.NewFormatter()
  60. // sub.formatter[embyM.GetFormatterName()] = embyM
  61. //
  62. // return &sub
  63. //}
  64. //
  65. //func (s SubTimelineFixerHelper) FixRecentlyItemsSubTimeline(movieRootDir, seriesRootDir string) error {
  66. //
  67. // // 首先得开启,不然就直接跳过不执行
  68. // if s.EmbyConfig.FixTimeLine == false {
  69. // s.log.Debugf("EmbyConfig.FixTimeLine = false, Skip")
  70. // return nil
  71. // }
  72. //
  73. // movieList, seriesList, err := s.embyHelper.GetRecentlyAddVideoListWithNoChineseSubtitle(movieRootDir, seriesRootDir)
  74. // if err != nil {
  75. // return err
  76. // }
  77. //
  78. // // 输出调试信息
  79. // s.log.Debugln("FixRecentlyItemsSubTimeline - DebugInfo - movieList Start")
  80. // for s, value := range movieList {
  81. // s.log.Debugln(s, value)
  82. // }
  83. // s.log.Debugln("FixRecentlyItemsSubTimeline - DebugInfo - movieList End")
  84. //
  85. // s.log.Debugln("FixRecentlyItemsSubTimeline - DebugInfo - seriesList Start")
  86. // for s, _ := range seriesList {
  87. // s.log.Debugln(s)
  88. // }
  89. // s.log.Debugln("FixRecentlyItemsSubTimeline - DebugInfo - seriesList End")
  90. //
  91. // s.log.Debugln("Start movieList fix Timeline")
  92. // // 先做电影的字幕校正、然后才是连续剧的
  93. // for _, info := range movieList {
  94. // // path.Dir 在 Windows 有梗,所以换个方式获取路径
  95. // videoRootPath := filepath.Dir(info.PhysicalVideoFileFullPath)
  96. // err = s.fixOneVideoSub(info.VideoInfo.Id, videoRootPath)
  97. // if err != nil {
  98. // return err
  99. // }
  100. // }
  101. // s.log.Debugln("End movieList fix Timeline")
  102. //
  103. // s.log.Debugln("Start seriesList fix Timeline")
  104. // for _, infos := range seriesList {
  105. // for _, info := range infos {
  106. // // path.Dir 在 Windows 有梗,所以换个方式获取路径
  107. // videoRootPath := filepath.Dir(info.PhysicalVideoFileFullPath)
  108. // err = s.fixOneVideoSub(info.VideoInfo.Id, videoRootPath)
  109. // if err != nil {
  110. // return err
  111. // }
  112. // }
  113. // }
  114. // s.log.Debugln("End seriesList fix Timeline")
  115. //
  116. // // 强制调用,测试 CGO=1 编译问题
  117. // s.log.Debugln("VAD Mode", vad.Mode)
  118. //
  119. // return nil
  120. //}
  121. //
  122. //func (s SubTimelineFixerHelper) fixOneVideoSub(videoId string, videoRootPath string) error {
  123. // s.log.Debugln("fixOneVideoSub VideoROotPath:", videoRootPath)
  124. // // internalEngSub 默认第一个是 srt 然后第二个是 ass,就不要去遍历了
  125. // found, internalEngSub, containChineseSubFile, err := s.embyHelper.GetInternalEngSubAndExChineseEnglishSub(videoId)
  126. // if err != nil {
  127. // return err
  128. // }
  129. //
  130. // if found == false {
  131. // s.log.Debugln("GetInternalEngSubAndExChineseEnglishSub - found == false")
  132. // return nil
  133. // }
  134. //
  135. // s.log.Debugln("internalEngSub:", len(internalEngSub), "containChineseSubFile:", len(containChineseSubFile))
  136. // // 需要先把原有的外置字幕带有 -fix 的删除,然后再做修正
  137. // // 不然如果调整了条件,之前修复的本次其实就不修正了,那么就会“残留”下来,误以为是本次配置的信息导致的
  138. // for _, exSubInfo := range containChineseSubFile {
  139. // // 没有编辑的就跳过
  140. // if strings.Contains(exSubInfo.FileName, sub_timeline_fixer.FixMask) == false {
  141. // continue
  142. // }
  143. //
  144. // subFileNeedRemove := filepath.Join(videoRootPath, exSubInfo.FileName)
  145. //
  146. // if videoRootPath == "" {
  147. // s.log.Debugln("videoRootPath == \"\", Skip Remove:", subFileNeedRemove)
  148. // continue
  149. // }
  150. //
  151. // s.log.Debugln("Remove fixed sub:", subFileNeedRemove)
  152. // err = os.Remove(subFileNeedRemove)
  153. // if err != nil {
  154. // return err
  155. // }
  156. // }
  157. //
  158. // // 从外置双语(中英)字幕中找对对应的内置 srt 字幕进行匹配比较
  159. // for _, exSubInfo := range containChineseSubFile {
  160. // inSelectSubIndex := 1
  161. // if exSubInfo.Ext == common.SubExtSRT {
  162. // inSelectSubIndex = 0
  163. // }
  164. // // 修正过的字幕有标记,将不会再次修复
  165. // if strings.Contains(exSubInfo.FileName, sub_timeline_fixer.FixMask) == true {
  166. // continue
  167. // }
  168. //
  169. // s.log.Debugln("fixSubTimeline start")
  170. // bFound, subFixInfos, subNewName, err := s.fixSubTimeline(internalEngSub[inSelectSubIndex], exSubInfo)
  171. // if err != nil {
  172. // return err
  173. // }
  174. // if bFound == false {
  175. // s.log.Debugln("fixSubTimeline bFound == false", exSubInfo.FileName)
  176. // continue
  177. // }
  178. // // 调试的时候用
  179. // if videoRootPath == "" {
  180. // s.log.Debugln("videoRootPath == \"\", Skip fix sub:", exSubInfo.FileName)
  181. // continue
  182. // }
  183. // for _, info := range subFixInfos {
  184. // // 写入 fix 后的字幕文件覆盖之前的字幕文件
  185. // desFixedSubFullName := filepath.Join(videoRootPath, subNewName)
  186. // err = s.saveSubFile(desFixedSubFullName, info.FixContent)
  187. // if err != nil {
  188. // return err
  189. // }
  190. // s.log.Infoln("Sub Timeline fixed:", desFixedSubFullName)
  191. // }
  192. // }
  193. //
  194. // return nil
  195. //}
  196. //
  197. //// fixSubTimeline 修复时间轴,containChineseSubFile 这里可能是,只要是带有中文的都算,简体、繁体、简英、繁英,需要后续额外的判断
  198. //func (s SubTimelineFixerHelper) fixSubTimeline(enSubFile emby.SubInfo, containChineseSubFile emby.SubInfo) (bool, []sub_timeline_fixer.SubFixInfo, string, error) {
  199. // fixedSubName := ""
  200. // s.log.Debugln("fixSubTimeline - DetermineFileTypeFromBytes", enSubFile.FileName)
  201. // bFind, infoBase, err := s.subParserHub.DetermineFileTypeFromBytes(enSubFile.Content, enSubFile.Ext)
  202. // if err != nil {
  203. // return false, nil, fixedSubName, err
  204. // }
  205. // if bFind == false {
  206. // return false, nil, fixedSubName, nil
  207. // }
  208. // infoBase.Name = enSubFile.FileName
  209. // /*
  210. // 这里发现一个梗,内置的英文字幕导出的时候,有可能需要合并多个 Dialogue,见
  211. // internal/pkg/sub_helper/sub_helper.go 中 MergeMultiDialogue4EngSubtitle 的实现
  212. // */
  213. // sub_helper.MergeMultiDialogue4EngSubtitle(infoBase)
  214. //
  215. // s.log.Debugln("fixSubTimeline - DetermineFileTypeFromBytes", containChineseSubFile.FileName)
  216. // bFind, infoSrc, err := s.subParserHub.DetermineFileTypeFromBytes(containChineseSubFile.Content, containChineseSubFile.Ext)
  217. // if err != nil {
  218. // return false, nil, fixedSubName, err
  219. // }
  220. // if bFind == false {
  221. // return false, nil, fixedSubName, nil
  222. // }
  223. // infoSrc.Name = containChineseSubFile.FileName
  224. // /*
  225. // 这里发现一个梗,内置的英文字幕导出的时候,有可能需要合并多个 Dialogue,见
  226. // internal/pkg/sub_helper/sub_helper.go 中 MergeMultiDialogue4EngSubtitle 的实现
  227. // */
  228. // sub_helper.MergeMultiDialogue4EngSubtitle(infoSrc)
  229. //
  230. // infoBaseNameWithOutExt := strings.Replace(infoBase.Name, path.Ext(infoBase.Name), "", -1)
  231. // //infoSrcNameWithOutExt := strings.Replace(infoSrc.Name, path.Ext(infoSrc.Name), "", -1)
  232. //
  233. // // 把原始的文件缓存下来,新建缓存的文件夹
  234. // subFixCacheRootPath, err := my_util.GetRootSubFixCacheFolder()
  235. // if err != nil {
  236. // return false, nil, fixedSubName, err
  237. // }
  238. // cacheTmpPath := filepath.Join(subFixCacheRootPath, infoBaseNameWithOutExt)
  239. // if my_util.IsDir(cacheTmpPath) == false {
  240. // err = os.MkdirAll(cacheTmpPath, os.ModePerm)
  241. // if err != nil {
  242. // return false, nil, fixedSubName, err
  243. // }
  244. // }
  245. // // 写入内置字幕、外置字幕原始文件
  246. // err = s.saveSubFile(filepath.Join(cacheTmpPath, infoBaseNameWithOutExt+".chinese(inside)"+infoBase.Ext), infoBase.Content)
  247. // if err != nil {
  248. // return false, nil, fixedSubName, err
  249. // }
  250. // err = s.saveSubFile(filepath.Join(cacheTmpPath, infoSrc.Name), infoSrc.Content)
  251. // if err != nil {
  252. // return false, nil, fixedSubName, err
  253. // }
  254. // bok, offsetTime, sd, err := s.subTimelineFixer.GetOffsetTimeV1(infoBase, infoSrc, filepath.Join(cacheTmpPath, infoSrc.Name+"-bar.html"), filepath.Join(cacheTmpPath, infoSrc.Name+".log"))
  255. // if offsetTime != 0 {
  256. // s.log.Infoln(infoSrc.Name, "offset time is", fmt.Sprintf("%f", offsetTime), "s")
  257. // }
  258. // // 超过 SD 阈值了
  259. // if sd > s.FixerConfig.V1_MaxStartTimeDiffSD {
  260. // s.log.Infoln(infoSrc.Name, "Start Time Diff SD, skip", fmt.Sprintf("%f", sd))
  261. // return false, nil, fixedSubName, nil
  262. // } else {
  263. // s.log.Infoln(infoSrc.Name, "Start Time Diff SD", fmt.Sprintf("%f", sd))
  264. // }
  265. //
  266. // if err != nil || bok == false {
  267. // return false, nil, fixedSubName, err
  268. // }
  269. //
  270. // // 偏移很小就无视了
  271. // if offsetTime < s.FixerConfig.V1_MinOffset && offsetTime > -s.FixerConfig.V1_MinOffset {
  272. // s.log.Infoln(infoSrc.Name, fmt.Sprintf("Min Offset Config is %f, skip ", s.FixerConfig.V1_MinOffset), fmt.Sprintf("now is %f", offsetTime))
  273. // return false, nil, fixedSubName, nil
  274. // }
  275. // // 写入校准时间轴后的字幕
  276. // var subFixInfos = make([]sub_timeline_fixer.SubFixInfo, 0)
  277. // for _, formatter := range s.formatter {
  278. // // 符合已知的字幕命名格式,不符合就跳过,都跳过也行,就不做任何操作而已
  279. // bMatch, fileNameWithOutExt, subExt, subLang, extraSubName := formatter.IsMatchThisFormat(infoSrc.Name)
  280. // if bMatch == false {
  281. // s.log.Debugln(fmt.Sprintf("%s IsMatchThisFormat == false, Skip, %s", formatter.GetFormatterName(), infoSrc.Name))
  282. // continue
  283. // }
  284. // // 是否包含 default 关键词,暂时无需判断 forced
  285. // hasDefault := false
  286. // if strings.Contains(strings.ToLower(infoSrc.Name), subparser.Sub_Ext_Mark_Default) == true {
  287. // hasDefault = true
  288. // }
  289. // // 生成对应字幕命名格式的,字幕命名。这里注意,normal 的时候, extraSubName+"-fix" 是无效的,不会被设置,也就是直接覆盖之前的字幕了。
  290. // subNewName, subNewNameDefault, _ := formatter.GenerateMixSubNameBase(fileNameWithOutExt, subExt, subLang, extraSubName+sub_timeline_fixer.FixMask)
  291. //
  292. // desFixSubFileFullPath := ""
  293. // if hasDefault == true {
  294. // fixedSubName = subNewNameDefault
  295. // desFixSubFileFullPath = filepath.Join(cacheTmpPath, subNewNameDefault)
  296. //
  297. // } else {
  298. // fixedSubName = subNewName
  299. // desFixSubFileFullPath = filepath.Join(cacheTmpPath, subNewName)
  300. // }
  301. // fixContent, err := s.subTimelineFixer.FixSubTimelineOneOffsetTime(infoSrc, offsetTime, desFixSubFileFullPath)
  302. // if err != nil {
  303. // return false, nil, fixedSubName, err
  304. // }
  305. // subFixInfos = append(subFixInfos, *sub_timeline_fixer.NewSubFixInfo(infoSrc.Name, fixContent))
  306. // }
  307. //
  308. // return true, subFixInfos, fixedSubName, nil
  309. //}
  310. //
  311. //func (s SubTimelineFixerHelper) saveSubFile(desSaveSubFileFullPath string, content string) error {
  312. // dstFile, err := os.Create(desSaveSubFileFullPath)
  313. // if err != nil {
  314. // return err
  315. // }
  316. // defer func() {
  317. // _ = dstFile.Close()
  318. // }()
  319. // _, err = dstFile.WriteString(content)
  320. // if err != nil {
  321. // return err
  322. // }
  323. //
  324. // return nil
  325. //}