sub_helper.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. package sub_helper
  2. import (
  3. "github.com/allanpk716/ChineseSubFinder/internal/common"
  4. "github.com/allanpk716/ChineseSubFinder/internal/pkg/archive_helper"
  5. "github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
  6. "github.com/allanpk716/ChineseSubFinder/internal/pkg/language"
  7. "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
  8. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  9. "github.com/allanpk716/ChineseSubFinder/internal/pkg/regex_things"
  10. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_parser_hub"
  11. "github.com/allanpk716/ChineseSubFinder/internal/types/subparser"
  12. "github.com/allanpk716/ChineseSubFinder/internal/types/supplier"
  13. "github.com/go-rod/rod/lib/utils"
  14. "io/ioutil"
  15. "math"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "time"
  21. )
  22. // OrganizeDlSubFiles 需要从汇总来是网站字幕中,解压对应的压缩包中的字幕出来
  23. func OrganizeDlSubFiles(tmpFolderName string, subInfos []supplier.SubInfo) (map[string][]string, error) {
  24. // 缓存列表,整理后的字幕列表
  25. // SxEx - []string 字幕的路径
  26. var siteSubInfoDict = make(map[string][]string)
  27. tmpFolderFullPath, err := my_util.GetTmpFolder(tmpFolderName)
  28. if err != nil {
  29. return nil, err
  30. }
  31. // 把后缀名给改好
  32. ChangeVideoExt2SubExt(subInfos)
  33. // 第三方的解压库,首先不支持 io.Reader 的操作,也就是得缓存到本地硬盘再读取解压
  34. // 且使用 walk 会无法解压 rar,得指定具体的实例,太麻烦了,直接用通用的接口得了,就是得都缓存下来再判断
  35. // 基于以上两点,写了一堆啰嗦的逻辑···
  36. for i := range subInfos {
  37. // 先存下来,保存是时候需要前缀,前缀就是从那个网站下载来的
  38. nowFileSaveFullPath := filepath.Join(tmpFolderFullPath, GetFrontNameAndOrgName(&subInfos[i]))
  39. err = utils.OutputFile(nowFileSaveFullPath, subInfos[i].Data)
  40. if err != nil {
  41. log_helper.GetLogger().Errorln("getFrontNameAndOrgName - OutputFile", subInfos[i].FromWhere, subInfos[i].Name, subInfos[i].TopN, err)
  42. continue
  43. }
  44. nowExt := strings.ToLower(subInfos[i].Ext)
  45. epsKey := my_util.GetEpisodeKeyName(subInfos[i].Season, subInfos[i].Episode)
  46. _, ok := siteSubInfoDict[epsKey]
  47. if ok == false {
  48. // 不存在则实例化
  49. siteSubInfoDict[epsKey] = make([]string, 0)
  50. }
  51. if nowExt != ".zip" && nowExt != ".tar" && nowExt != ".rar" && nowExt != ".7z" {
  52. // 是否是受支持的字幕类型
  53. if sub_parser_hub.IsSubExtWanted(nowExt) == false {
  54. continue
  55. }
  56. // 加入缓存列表
  57. siteSubInfoDict[epsKey] = append(siteSubInfoDict[epsKey], nowFileSaveFullPath)
  58. } else {
  59. // 那么就是需要解压的文件了
  60. // 解压,给一个单独的文件夹
  61. unzipTmpFolder := filepath.Join(tmpFolderFullPath, subInfos[i].FromWhere)
  62. err = os.MkdirAll(unzipTmpFolder, os.ModePerm)
  63. if err != nil {
  64. return nil, err
  65. }
  66. err = archive_helper.UnArchiveFile(nowFileSaveFullPath, unzipTmpFolder)
  67. // 解压完成后,遍历受支持的字幕列表,加入缓存列表
  68. if err != nil {
  69. log_helper.GetLogger().Errorln("archiver.UnArchive", subInfos[i].FromWhere, subInfos[i].Name, subInfos[i].TopN, err)
  70. continue
  71. }
  72. // 搜索这个目录下的所有符合字幕格式的文件
  73. subFileFullPaths, err := SearchMatchedSubFileByDir(unzipTmpFolder)
  74. if err != nil {
  75. log_helper.GetLogger().Errorln("searchMatchedSubFile", subInfos[i].FromWhere, subInfos[i].Name, subInfos[i].TopN, err)
  76. continue
  77. }
  78. // 这里需要给这些下载到的文件进行改名,加是从那个网站来的前缀,后续好查找
  79. for _, fileFullPath := range subFileFullPaths {
  80. newSubName := AddFrontName(subInfos[i], filepath.Base(fileFullPath))
  81. newSubNameFullPath := filepath.Join(tmpFolderFullPath, newSubName)
  82. // 改名
  83. err = os.Rename(fileFullPath, newSubNameFullPath)
  84. if err != nil {
  85. log_helper.GetLogger().Errorln("os.Rename", subInfos[i].FromWhere, subInfos[i].Name, subInfos[i].TopN, err)
  86. continue
  87. }
  88. // 加入缓存列表
  89. siteSubInfoDict[epsKey] = append(siteSubInfoDict[epsKey], newSubNameFullPath)
  90. }
  91. }
  92. }
  93. return siteSubInfoDict, nil
  94. }
  95. // ChangeVideoExt2SubExt 检测 Name,如果是视频的后缀名就改为字幕的后缀名
  96. func ChangeVideoExt2SubExt(subInfos []supplier.SubInfo) {
  97. for x, info := range subInfos {
  98. tmpSubFileName := info.Name
  99. // 如果后缀名是下载字幕目标的后缀名 或者 是压缩包格式的,则跳过
  100. if strings.Contains(tmpSubFileName, info.Ext) == true || archive_helper.IsWantedArchiveExtName(tmpSubFileName) == true {
  101. } else {
  102. subInfos[x].Name = tmpSubFileName + info.Ext
  103. }
  104. }
  105. }
  106. // SelectChineseBestBilingualSubtitle 找到合适的双语中文字幕,简体->繁体,以及 字幕类型的优先级选择
  107. func SelectChineseBestBilingualSubtitle(subs []subparser.FileInfo, subTypePriority int) *subparser.FileInfo {
  108. // 先傻一点实现优先双语的,之前的写法有 bug
  109. for _, info := range subs {
  110. // 找到了中文字幕
  111. if language.HasChineseLang(info.Lang) == true {
  112. // 字幕的优先级 0 - 原样, 1 - srt , 2 - ass/ssa
  113. if subTypePriority == 1 {
  114. // 1 - srt
  115. if strings.ToLower(info.Ext) == common.SubExtSRT {
  116. // 优先双语
  117. if language.IsBilingualSubtitle(info.Lang) == true {
  118. return &info
  119. }
  120. }
  121. } else if subTypePriority == 2 {
  122. // 2 - ass/ssa
  123. if strings.ToLower(info.Ext) == common.SubExtASS || strings.ToLower(info.Ext) == common.SubExtSSA {
  124. // 优先双语
  125. if language.IsBilingualSubtitle(info.Lang) == true {
  126. return &info
  127. }
  128. }
  129. } else {
  130. // 优先双语
  131. if language.IsBilingualSubtitle(info.Lang) == true {
  132. return &info
  133. }
  134. }
  135. }
  136. }
  137. return nil
  138. }
  139. // SelectChineseBestSubtitle 找到合适的中文字幕,简体->繁体,以及 字幕类型的优先级选择
  140. func SelectChineseBestSubtitle(subs []subparser.FileInfo, subTypePriority int) *subparser.FileInfo {
  141. // 先傻一点实现优先双语的,之前的写法有 bug
  142. for _, info := range subs {
  143. // 找到了中文字幕
  144. if language.HasChineseLang(info.Lang) == true {
  145. // 字幕的优先级 0 - 原样, 1 - srt , 2 - ass/ssa
  146. if subTypePriority == 1 {
  147. // 1 - srt
  148. if strings.ToLower(info.Ext) == common.SubExtSRT {
  149. return &info
  150. }
  151. } else if subTypePriority == 2 {
  152. // 2 - ass/ssa
  153. if strings.ToLower(info.Ext) == common.SubExtASS || strings.ToLower(info.Ext) == common.SubExtSSA {
  154. return &info
  155. }
  156. } else {
  157. return &info
  158. }
  159. }
  160. }
  161. return nil
  162. }
  163. // GetFrontNameAndOrgName 返回的名称包含,那个网站下载的,这个网站中排名第几,文件名
  164. func GetFrontNameAndOrgName(info *supplier.SubInfo) string {
  165. infoName := ""
  166. fileName, err := decode.GetVideoInfoFromFileName(info.Name)
  167. if err != nil {
  168. log_helper.GetLogger().Warnln("", err)
  169. infoName = info.Name
  170. } else {
  171. infoName = fileName.Title + "_S" + strconv.Itoa(fileName.Season) + "E" + strconv.Itoa(fileName.Episode) + filepath.Ext(info.Name)
  172. }
  173. info.Name = infoName
  174. return "[" + info.FromWhere + "]_" + strconv.FormatInt(info.TopN, 10) + "_" + infoName
  175. }
  176. // AddFrontName 添加文件的前缀
  177. func AddFrontName(info supplier.SubInfo, orgName string) string {
  178. return "[" + info.FromWhere + "]_" + strconv.FormatInt(info.TopN, 10) + "_" + orgName
  179. }
  180. // SearchMatchedSubFileByDir 搜索符合后缀名的视频文件,排除 Sub_SxE0 这样的文件夹中的文件
  181. func SearchMatchedSubFileByDir(dir string) ([]string, error) {
  182. // 这里有个梗,会出现 __MACOSX 这类文件夹,那么里面会有一样的文件,需要用文件大小排除一下,至少大于 1 kb 吧
  183. var fileFullPathList = make([]string, 0)
  184. pathSep := string(os.PathSeparator)
  185. files, err := ioutil.ReadDir(dir)
  186. if err != nil {
  187. return nil, err
  188. }
  189. for _, curFile := range files {
  190. fullPath := dir + pathSep + curFile.Name()
  191. if curFile.IsDir() {
  192. // 需要排除 Sub_S1E0、Sub_S2E0 这样的整季的字幕文件夹,这里仅仅是缓存,不会被加载的
  193. matched := regex_things.RegOneSeasonSubFolderNameMatch.FindAllStringSubmatch(curFile.Name(), -1)
  194. if len(matched) > 0 {
  195. continue
  196. }
  197. // 内层的错误就无视了
  198. oneList, _ := SearchMatchedSubFileByDir(fullPath)
  199. if oneList != nil {
  200. fileFullPathList = append(fileFullPathList, oneList...)
  201. }
  202. } else {
  203. // 这里就是文件了
  204. if curFile.Size() < 1000 {
  205. continue
  206. }
  207. if sub_parser_hub.IsSubExtWanted(filepath.Ext(curFile.Name())) == true {
  208. fileFullPathList = append(fileFullPathList, fullPath)
  209. }
  210. }
  211. }
  212. return fileFullPathList, nil
  213. }
  214. // SearchMatchedSubFileByOneVideo 搜索这个视频当前目录下匹配的字幕
  215. func SearchMatchedSubFileByOneVideo(oneVideoFullPath string) ([]string, error) {
  216. dir := filepath.Dir(oneVideoFullPath)
  217. fileName := filepath.Base(oneVideoFullPath)
  218. fileName = strings.ToLower(fileName)
  219. fileName = strings.ReplaceAll(fileName, filepath.Ext(fileName), "")
  220. pathSep := string(os.PathSeparator)
  221. files, err := ioutil.ReadDir(dir)
  222. if err != nil {
  223. return nil, err
  224. }
  225. var matchedSubs = make([]string, 0)
  226. for _, curFile := range files {
  227. if curFile.IsDir() {
  228. continue
  229. }
  230. // 这里就是文件了
  231. if curFile.Size() < 1000 {
  232. continue
  233. }
  234. // 判断的时候用小写的,后续重命名的时候用原有的名称
  235. nowFileName := strings.ToLower(curFile.Name())
  236. // 后缀名得对
  237. if sub_parser_hub.IsSubExtWanted(filepath.Ext(nowFileName)) == false {
  238. continue
  239. }
  240. // 字幕文件名应该包含 视频文件名(无后缀)
  241. if strings.Contains(nowFileName, fileName) == false {
  242. continue
  243. }
  244. oldPath := dir + pathSep + curFile.Name()
  245. matchedSubs = append(matchedSubs, oldPath)
  246. }
  247. return matchedSubs, nil
  248. }
  249. // SearchVideoMatchSubFileAndRemoveExtMark 找到找个视频目录下相匹配的字幕,同时去除这些字幕中 .default 或者 .forced 的标记。注意这两个标记不应该同时出现,否则无法正确去除
  250. func SearchVideoMatchSubFileAndRemoveExtMark(oneVideoFullPath string) error {
  251. dir := filepath.Dir(oneVideoFullPath)
  252. fileName := filepath.Base(oneVideoFullPath)
  253. fileName = strings.ToLower(fileName)
  254. fileName = strings.ReplaceAll(fileName, filepath.Ext(fileName), "")
  255. pathSep := string(os.PathSeparator)
  256. files, err := ioutil.ReadDir(dir)
  257. if err != nil {
  258. return err
  259. }
  260. for _, curFile := range files {
  261. if curFile.IsDir() {
  262. continue
  263. } else {
  264. // 这里就是文件了
  265. if curFile.Size() < 1000 {
  266. continue
  267. }
  268. // 判断的时候用小写的,后续重命名的时候用原有的名称
  269. nowFileName := strings.ToLower(curFile.Name())
  270. // 后缀名得对
  271. if sub_parser_hub.IsSubExtWanted(filepath.Ext(nowFileName)) == false {
  272. continue
  273. }
  274. // 字幕文件名应该包含 视频文件名(无后缀)
  275. if strings.Contains(nowFileName, fileName) == false {
  276. continue
  277. }
  278. // 得包含 .default. 找个关键词
  279. if strings.Contains(nowFileName, subparser.Sub_Ext_Mark_Default+".") == true {
  280. oldPath := dir + pathSep + curFile.Name()
  281. newPath := dir + pathSep + strings.ReplaceAll(curFile.Name(), subparser.Sub_Ext_Mark_Default+".", ".")
  282. err = os.Rename(oldPath, newPath)
  283. if err != nil {
  284. return err
  285. }
  286. } else if strings.Contains(nowFileName, subparser.Sub_Ext_Mark_Forced+".") == true {
  287. // 得包含 .forced. 找个关键词
  288. oldPath := dir + pathSep + curFile.Name()
  289. newPath := dir + pathSep + strings.ReplaceAll(curFile.Name(), subparser.Sub_Ext_Mark_Forced+".", ".")
  290. err = os.Rename(oldPath, newPath)
  291. if err != nil {
  292. return err
  293. }
  294. } else {
  295. continue
  296. }
  297. }
  298. }
  299. return nil
  300. }
  301. // DeleteOneSeasonSubCacheFolder 删除一个连续剧中的所有一季字幕的缓存文件夹
  302. func DeleteOneSeasonSubCacheFolder(seriesDir string) error {
  303. files, err := ioutil.ReadDir(seriesDir)
  304. if err != nil {
  305. return err
  306. }
  307. pathSep := string(os.PathSeparator)
  308. for _, curFile := range files {
  309. if curFile.IsDir() == true {
  310. matched := regex_things.RegOneSeasonSubFolderNameMatch.FindAllStringSubmatch(curFile.Name(), -1)
  311. if matched == nil || len(matched) < 1 {
  312. continue
  313. }
  314. fullPath := seriesDir + pathSep + curFile.Name()
  315. err = os.RemoveAll(fullPath)
  316. if err != nil {
  317. return err
  318. }
  319. }
  320. }
  321. return nil
  322. }
  323. /*
  324. 只针对英文字幕进行合并分散的 Dialogues
  325. 会遇到这样的字幕,如下0
  326. 2line-The Card Counter (2021) WEBDL-1080p.chinese(inside).ass
  327. 它的对白一句话分了两个 dialogue 去做。这样做后续字幕时间轴校正就会遇到问题,因为只有一半,匹配占比会很低
  328. (每一个 Dialogue 的首字母需要分析,大写和小写的占比是多少,统计一下,正常的,和上述特殊的)
  329. 那么,就需要额外的逻辑去对 DialoguesEx 进行额外的推断
  330. 暂时考虑的方案是,英文对白每一句的开头应该是英文大写字幕,如果是小写字幕,就应该与上语句合并,且每一句的字符长度有大于一定才触发
  331. */
  332. func MergeMultiDialogue4EngSubtitle(inSubParser *subparser.FileInfo) {
  333. merger := NewDialogueMerger()
  334. for _, dialogueEx := range inSubParser.DialoguesEx {
  335. merger.Add(dialogueEx)
  336. }
  337. inSubParser.DialoguesEx = merger.Get()
  338. }
  339. // GetVADINfoFromSub 跟下面的 GetVADINfoFromSubNeedOffsetTimeWillInsert 函数功能一致
  340. func GetVADINfoFromSub(infoSrc *subparser.FileInfo, FrontAndEndPer float64, SubUnitMaxCount int, insert bool, kf *KeyFeatures) ([]SubUnit, error) {
  341. return GetVADINfoFromSubNeedOffsetTimeWillInsert(infoSrc, FrontAndEndPer, SubUnitMaxCount, 0, insert, kf)
  342. }
  343. /*
  344. GetVADINfoFromSubNeedOffsetTimeWillInsert 只不过这里可以加一个每一句话固定的偏移时间
  345. 这里的字幕要求是完整的一个字幕
  346. 1. 抽取字幕的时间片段的时候,暂定,前 15% 和后 15% 要避开,前奏、主题曲、结尾曲
  347. 2. 将整个字幕,抽取连续 5 句对话为一个单元,提取时间片段信息
  348. 3. 可能还有一个需求,默认的模式是每五句话一个单元,还有一种模式是每一句话向后找到连续的四句话组成一个单元,允许重叠
  349. 目前看到的情况是前者的抽样率太低,需要使用后者的逻辑
  350. */
  351. func GetVADINfoFromSubNeedOffsetTimeWillInsert(infoSrc *subparser.FileInfo, SkipFrontAndEndPer float64, SubUnitMaxCount int, offsetTime float64, insert bool, kf *KeyFeatures) ([]SubUnit, error) {
  352. if SubUnitMaxCount < 0 {
  353. SubUnitMaxCount = 0
  354. }
  355. srcSubUnitList := make([]SubUnit, 0)
  356. srcSubDialogueList := make([]subparser.OneDialogueEx, 0)
  357. srcOneSubUnit := NewSubUnit()
  358. // srcDuration
  359. lastDialogueExTimeEnd, err := infoSrc.ParseTime(infoSrc.DialoguesEx[len(infoSrc.DialoguesEx)-1].EndTime)
  360. if err != nil {
  361. return nil, err
  362. }
  363. srcDuration := my_util.Time2SecendNumber(lastDialogueExTimeEnd)
  364. for i := 0; i < len(infoSrc.DialoguesEx); i++ {
  365. oneDialogueExTimeStart, err := infoSrc.ParseTime(infoSrc.DialoguesEx[i].StartTime)
  366. if err != nil {
  367. return nil, err
  368. }
  369. oneDialogueExTimeEnd, err := infoSrc.ParseTime(infoSrc.DialoguesEx[i].EndTime)
  370. if err != nil {
  371. return nil, err
  372. }
  373. oneStart := my_util.Time2SecendNumber(oneDialogueExTimeStart)
  374. if SkipFrontAndEndPer > 0 {
  375. if srcDuration*SkipFrontAndEndPer > oneStart || srcDuration*(1.0-SkipFrontAndEndPer) < oneStart {
  376. continue
  377. }
  378. }
  379. // 如果当前的这一句话,为空,或者进过正则表达式剔除特殊字符后为空,则跳过
  380. if my_util.ReplaceSpecString(infoSrc.GetDialogueExContent(i), "") == "" {
  381. continue
  382. }
  383. // 低于 5句对白,则添加
  384. if srcOneSubUnit.GetDialogueCount() < SubUnitMaxCount {
  385. // 算上偏移
  386. offsetTimeDuration := time.Duration(offsetTime * math.Pow10(9))
  387. oneDialogueExTimeStart = oneDialogueExTimeStart.Add(offsetTimeDuration)
  388. oneDialogueExTimeEnd = oneDialogueExTimeEnd.Add(offsetTimeDuration)
  389. // 如果没有偏移就是 0
  390. if insert == true {
  391. srcOneSubUnit.AddAndInsert(oneDialogueExTimeStart, oneDialogueExTimeEnd)
  392. } else {
  393. srcOneSubUnit.Add(oneDialogueExTimeStart, oneDialogueExTimeEnd)
  394. }
  395. // 这一个单元的 Dialogue 需要合并起来,才能判断是否符合“钥匙”的要求
  396. srcSubDialogueList = append(srcSubDialogueList, infoSrc.DialoguesEx[i])
  397. } else {
  398. // 筹够那么多句话了,需要判断一次是否符合“钥匙”的要求
  399. tmpNowMatchKey := IsMatchKey(srcSubDialogueList, kf)
  400. srcOneSubUnit.IsMatchKey = tmpNowMatchKey
  401. // 用完清空
  402. srcSubDialogueList = make([]subparser.OneDialogueEx, 0)
  403. // 将拼凑起来的对话组成一个单元进行存储起来
  404. srcSubUnitList = append(srcSubUnitList, *srcOneSubUnit)
  405. // 然后重置
  406. srcOneSubUnit = NewSubUnit()
  407. // TODO 这里决定了插入数据的密度,有待测试
  408. //i = i - SubUnitMaxCount
  409. if kf == nil {
  410. // 走原始的逻辑 i 的赋值逻辑跟之前一样,需要每一次进一步,也就是有重叠的部分出现
  411. //i = i - SubUnitMaxCount + SubUnitMaxCount/5
  412. //i = i - SubUnitMaxCount + SubUnitMaxCount/2
  413. i = i - SubUnitMaxCount
  414. } else {
  415. if tmpNowMatchKey == false {
  416. // 走原始的逻辑 i 的赋值逻辑跟之前一样,需要每一次进一步,也就是有重叠的部分出现
  417. i = i - SubUnitMaxCount
  418. } else {
  419. // 判断了“钥匙”特征,且通过了
  420. // i 需要跳过当前已经覆盖的段
  421. i = i - SubUnitMaxCount + SubUnitMaxCount/2
  422. }
  423. }
  424. }
  425. }
  426. if srcOneSubUnit.GetDialogueCount() > 0 {
  427. srcSubUnitList = append(srcSubUnitList, *srcOneSubUnit)
  428. }
  429. return srcSubUnitList, nil
  430. }
  431. // IsMatchKey 是否符合“钥匙”的标准
  432. func IsMatchKey(srcSubDialogueList []subparser.OneDialogueEx, kf *KeyFeatures) bool {
  433. if kf == nil {
  434. return false
  435. }
  436. /*
  437. 这里是设置主要依赖的还是数据源,源必须有足够的对白(暂定 50 句),才可能找到这么多信息
  438. 这里需要匹配的“钥匙”特征,先简单实现为 (这三个需要不交叉时间段)
  439. 1. 大坑(大于 10s 的对白间隔)至少 1 个
  440. 2. 中坑(大于 2 且小于 5s 的对白间隔)至少 3 个
  441. 3. 小坑(大于 1 且小于 2s 的对白间隔)至少 5 个
  442. */
  443. dialogueIntervals := make([]float64, 0)
  444. tmpFileInfo := subparser.FileInfo{}
  445. // 现在需要进行凹坑的识别,一共由多少个,间隔多少
  446. for i := 0; i < len(srcSubDialogueList)-1; i++ {
  447. startTime, err := tmpFileInfo.ParseTime(srcSubDialogueList[i+1].StartTime)
  448. if err != nil {
  449. return false
  450. }
  451. endTime, err := tmpFileInfo.ParseTime(srcSubDialogueList[i].EndTime)
  452. if err != nil {
  453. return false
  454. }
  455. // 对话间的时间间隔
  456. dialogueIntervals = append(dialogueIntervals, my_util.Time2SecendNumber(startTime)-my_util.Time2SecendNumber(endTime))
  457. }
  458. // big
  459. for _, value := range dialogueIntervals {
  460. if kf.Big.Match(value) == true {
  461. kf.Big.NowCount++
  462. }
  463. if kf.Middle.Match(value) == true {
  464. kf.Middle.NowCount++
  465. }
  466. if kf.Small.Match(value) == true {
  467. kf.Small.NowCount++
  468. }
  469. }
  470. // 统计到的要 >= 目标的个数
  471. if kf.Big.NowCount < kf.Big.LeastCount || kf.Middle.NowCount < kf.Middle.LeastCount || kf.Small.NowCount < kf.Small.LeastCount {
  472. return false
  473. }
  474. return true
  475. }