language.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package model
  2. import (
  3. "github.com/abadojack/whatlanggo"
  4. "github.com/allanpk716/ChineseSubFinder/common"
  5. "github.com/axgle/mahonia"
  6. "github.com/saintfish/chardet"
  7. "strings"
  8. )
  9. // LangConverter 语言转换器
  10. func LangConverter(subLang string) common.Language {
  11. /*
  12. xunlei:未知语言、简体&英语、繁体&英语、简体、繁体、英语
  13. */
  14. if strings.Contains(subLang, common.MatchLangChs) {
  15. // 优先简体
  16. if strings.Contains(subLang, common.MatchLangEn) {
  17. // 简英
  18. return common.ChineseSimpleEnglish
  19. } else if strings.Contains(subLang, common.MatchLangJp) {
  20. // 简日
  21. return common.ChineseSimpleJapanese
  22. } else if strings.Contains(subLang, common.MatchLangKr) {
  23. // 简韩
  24. return common.ChineseSimpleKorean
  25. }
  26. // 默认简体中文
  27. return common.ChineseSimple
  28. } else if strings.Contains(subLang, common.MatchLangCht) {
  29. // 然后是繁体
  30. if strings.Contains(subLang, common.MatchLangEn) {
  31. // 繁英
  32. return common.ChineseTraditionalEnglish
  33. } else if strings.Contains(subLang, common.MatchLangJp) {
  34. // 繁日
  35. return common.ChineseTraditionalJapanese
  36. } else if strings.Contains(subLang, common.MatchLangKr) {
  37. // 繁韩
  38. return common.ChineseTraditionalKorean
  39. }
  40. // 默认繁体中文
  41. return common.ChineseTraditional
  42. } else if strings.Contains(subLang, common.MatchLangEn) {
  43. // 英文
  44. return common.English
  45. } else if strings.Contains(subLang, common.MatchLangJp) {
  46. // 日文
  47. return common.Japanese
  48. } else if strings.Contains(subLang, common.MatchLangKr) {
  49. // 韩文
  50. return common.Korean
  51. } else {
  52. // 都没有,则标记未知
  53. return common.Unknow
  54. }
  55. }
  56. // HasChineseLang 是否包含中文
  57. func HasChineseLang(lan common.Language) bool {
  58. switch lan {
  59. case common.ChineseSimple,
  60. common.ChineseTraditional,
  61. common.ChineseSimpleEnglish,
  62. common.ChineseTraditionalEnglish,
  63. common.ChineseSimpleJapanese,
  64. common.ChineseTraditionalJapanese,
  65. common.ChineseSimpleKorean,
  66. common.ChineseTraditionalKorean:
  67. return true
  68. default:
  69. return false
  70. }
  71. }
  72. // IsBilingualSubtitle 是否是双语字幕
  73. func IsBilingualSubtitle(lan common.Language) bool {
  74. switch lan {
  75. case common.ChineseSimpleEnglish,
  76. common.ChineseTraditionalEnglish,
  77. common.ChineseSimpleJapanese,
  78. common.ChineseTraditionalJapanese,
  79. common.ChineseSimpleKorean,
  80. common.ChineseTraditionalKorean:
  81. return true
  82. default:
  83. return false
  84. }
  85. }
  86. // Lang2EmbyName 从语言转换到 Emby 能够识别的字幕命名
  87. func Lang2EmbyName(lan common.Language) string {
  88. switch lan {
  89. case common.Unknow: // 未知语言
  90. return common.Emby_unknow
  91. case common.ChineseSimple: // 简体中文
  92. return common.Emby_chs
  93. case common.ChineseTraditional: // 繁体中文
  94. return common.Emby_cht
  95. case common.ChineseSimpleEnglish: // 简英双语字幕
  96. return common.Emby_chs_en
  97. case common.ChineseTraditionalEnglish: // 繁英双语字幕
  98. return common.Emby_cht_en
  99. case common.English: // 英文
  100. return common.Emby_en
  101. case common.Japanese: // 日语
  102. return common.Emby_jp
  103. case common.ChineseSimpleJapanese: // 简日双语字幕
  104. return common.Emby_chs_jp
  105. case common.ChineseTraditionalJapanese: // 繁日双语字幕
  106. return common.Emby_cht_jp
  107. case common.Korean: // 韩语
  108. return common.Emby_kr
  109. case common.ChineseSimpleKorean: // 简韩双语字幕
  110. return common.Emby_chs_kr
  111. case common.ChineseTraditionalKorean: // 繁韩双语字幕
  112. return common.Emby_cht_kr
  113. default:
  114. return common.Emby_unknow
  115. }
  116. }
  117. // GetLangOptions 语言识别的 Options Whitelist
  118. func GetLangOptions() whatlanggo.Options {
  119. return whatlanggo.Options{
  120. Whitelist: map[whatlanggo.Lang]bool{
  121. whatlanggo.Cmn: true, // 中文 11
  122. whatlanggo.Eng: true, // 英文 15
  123. whatlanggo.Jpn: true, // 日文 32
  124. whatlanggo.Kor: true, // 韩文 37
  125. },
  126. }
  127. }
  128. // IsWhiteListLang 是否是白名单语言
  129. func IsWhiteListLang(lang whatlanggo.Lang) bool {
  130. switch lang {
  131. // 中文 英文 日文 韩文
  132. case whatlanggo.Cmn, whatlanggo.Eng,whatlanggo.Jpn,whatlanggo.Kor:
  133. return true
  134. default:
  135. return false
  136. }
  137. }
  138. // DetectSubLangAndStatistics 检测语言然后统计
  139. func DetectSubLangAndStatistics(lines []string, langDict map[int]int) {
  140. for _, line := range lines {
  141. info := whatlanggo.DetectWithOptions(line, GetLangOptions())
  142. tmpLang := -1
  143. if IsWhiteListLang(info.Lang) == true {
  144. tmpLang = (int)(info.Lang)
  145. }
  146. // 这一种语言的 key 是否存在,不存在则新建,存在再数值 +1
  147. value, ok := langDict[tmpLang]
  148. if ok == true {
  149. // 累加
  150. value++
  151. langDict[tmpLang] = value
  152. } else {
  153. langDict[tmpLang] = 1
  154. }
  155. }
  156. }
  157. // SubLangStatistics2SubLangType 由分析的信息转换为具体是什么字幕的语言类型
  158. func SubLangStatistics2SubLangType(countLineFeed, AllLines float32, langDict map[int]int) common.Language {
  159. const basePer = 0.8
  160. // 是否是双语?
  161. isDouble := false
  162. perLines := countLineFeed / AllLines
  163. // 第二行字幕出现的概率大于 80% 应该稳了吧,不然还能三语?
  164. if perLines > basePer {
  165. isDouble = true
  166. }
  167. // TODO 现在是没有很好的办法去识别是简体还是繁体中文的,所以···
  168. // 中文
  169. countChinese, hasChinese := langDict[int(whatlanggo.Cmn)]
  170. // 英文
  171. countEnglish, hasEnglish := langDict[int(whatlanggo.Eng)]
  172. // 日文
  173. countJapanese, hasJapanese := langDict[int(whatlanggo.Jpn)]
  174. // 韩文
  175. countKorean, hasKorean := langDict[int(whatlanggo.Kor)]
  176. // 优先判断双语
  177. if isDouble == true {
  178. // 首先得在外面统计就知道是双语
  179. if hasChinese && hasEnglish {
  180. // 简体 英文
  181. return common.ChineseSimpleEnglish
  182. } else if hasChinese && hasJapanese {
  183. // 简体 日文
  184. return common.ChineseSimpleJapanese
  185. } else if hasChinese && hasKorean {
  186. // 简体 韩文
  187. return common.ChineseSimpleKorean
  188. } else if hasChinese {
  189. return common.ChineseSimple
  190. } else if hasEnglish {
  191. return common.English
  192. } else if hasJapanese {
  193. return common.Japanese
  194. } else if hasKorean {
  195. return common.Korean
  196. } else {
  197. return common.Unknow
  198. }
  199. } else {
  200. // 如果比例达不到,那么就是单语言,所以最多的那个就是当前的语言
  201. // 这里的字典是有可能出现
  202. if hasChinese {
  203. // 那么起码要占比 80% 对吧
  204. perLines = float32(countChinese) / AllLines
  205. if perLines > basePer {
  206. return common.ChineseSimple
  207. }
  208. }
  209. if hasEnglish {
  210. // 那么起码要占比 80% 对吧
  211. perLines = float32(countEnglish) / AllLines
  212. if perLines > basePer {
  213. return common.English
  214. }
  215. }
  216. if hasJapanese {
  217. // 那么起码要占比 80% 对吧
  218. perLines = float32(countJapanese) / AllLines
  219. if perLines > basePer {
  220. return common.Japanese
  221. }
  222. }
  223. if hasKorean {
  224. // 那么起码要占比 80% 对吧
  225. perLines = float32(countKorean) / AllLines
  226. if perLines > basePer {
  227. return common.Korean
  228. }
  229. }
  230. return common.Unknow
  231. }
  232. }
  233. // IsChineseSimpleOrTraditional 从字幕的文件名称中尝试确认是简体还是繁体,不需要判断双语问题,有额外的解析器完成。只可能出现 ChineseSimple ChineseTraditional Unknow 三种情况
  234. func IsChineseSimpleOrTraditional(inputFileName string, orgLang common.Language) common.Language {
  235. if strings.Contains(inputFileName, common.SubNameKeywordChineseSimple) || strings.Contains(inputFileName, common.MatchLangChs) {
  236. // 简体中文关键词的匹配
  237. return orgLang
  238. } else if strings.Contains(inputFileName, common.SubNameKeywordTraditional) || strings.Contains(inputFileName, common.MatchLangCht) {
  239. // 繁体中文关键词的匹配
  240. if orgLang == common.ChineseSimple {
  241. // 简体 -> 繁体
  242. return common.ChineseTraditional
  243. } else if orgLang == common.ChineseSimpleEnglish {
  244. // 简体英文 -> 繁体英文
  245. return common.ChineseTraditionalEnglish
  246. } else if orgLang == common.ChineseSimpleJapanese {
  247. // 简体日文 -> 繁体日文
  248. return common.ChineseTraditionalJapanese
  249. } else if orgLang == common.ChineseSimpleKorean {
  250. // 简体韩文 -> 繁体韩文
  251. return common.ChineseTraditionalKorean
  252. }
  253. // 进来了都不是,那么就返回原来的语言
  254. return orgLang
  255. } else {
  256. // 都没有匹配上,返回原来识别出来的类型即可
  257. return orgLang
  258. }
  259. }
  260. // ConvertToString 将字符串从原始编码转换到目标编码,需要配合字符串检测编码库使用 chardet.NewTextDetector()
  261. func ConvertToString(src string, srcCode string, tagCode string) string {
  262. defer func() {
  263. if err := recover(); err != nil {
  264. GetLogger().Errorln("ConvertToString panic:", err)
  265. }
  266. }()
  267. srcCoder := mahonia.NewDecoder(srcCode)
  268. srcResult := srcCoder.ConvertString(src)
  269. tagCoder := mahonia.NewDecoder(tagCode)
  270. _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
  271. result := string(cdata)
  272. return result
  273. }
  274. // ChangeFileCoding2UTF8 自动检测文件的编码,然后转换到 UTF-8
  275. func ChangeFileCoding2UTF8(inBytes []byte) ([]byte, error) {
  276. detector := chardet.NewTextDetector()
  277. result, err := detector.DetectBest(inBytes)
  278. if err != nil {
  279. return nil ,err
  280. }
  281. ouBytes := inBytes
  282. if result.Charset != "UTF-8" {
  283. ouString := ConvertToString(string(inBytes), result.Charset, "UTF-8")
  284. ouBytes = []byte(ouString)
  285. }
  286. return ouBytes, nil
  287. }
  288. // FindChineseBestSubtitle 找到合适的中文字幕,优先简体双语,简体->繁体
  289. func FindChineseBestSubtitle(subs []common.SubParserFileInfo) *common.SubParserFileInfo {
  290. for _, info := range subs {
  291. // 找到了中文字幕
  292. if HasChineseLang(info.Lang) == true {
  293. // 优先双语
  294. if IsBilingualSubtitle(info.Lang) == true {
  295. return &info
  296. }
  297. return &info
  298. }
  299. }
  300. return nil
  301. }