language.go 11 KB

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