decode.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package pkg
  2. import (
  3. "errors"
  4. common2 "github.com/allanpk716/ChineseSubFinder/internal/common"
  5. "github.com/allanpk716/ChineseSubFinder/internal/types"
  6. "github.com/beevik/etree"
  7. PTN "github.com/middelink/go-parse-torrent-name"
  8. "io/ioutil"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "regexp"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. func getImdbAndYearMovieXml(movieFilePath string) (types.VideoIMDBInfo, error) {
  18. videoInfo := types.VideoIMDBInfo{}
  19. doc := etree.NewDocument()
  20. if err := doc.ReadFromFile(movieFilePath); err != nil {
  21. return videoInfo, err
  22. }
  23. for _, t := range doc.FindElements("//IMDB") {
  24. videoInfo.ImdbId = t.Text()
  25. break
  26. }
  27. for _, t := range doc.FindElements("//ProductionYear") {
  28. videoInfo.Year = t.Text()
  29. break
  30. }
  31. if videoInfo.ImdbId != "" {
  32. return videoInfo, nil
  33. }
  34. return videoInfo, common2.CanNotFindIMDBID
  35. }
  36. func getImdbAndYearNfo(nfoFilePath string, rootKey string) (types.VideoIMDBInfo, error) {
  37. imdbInfo := types.VideoIMDBInfo{}
  38. doc := etree.NewDocument()
  39. // 这里会遇到一个梗,下面的关键词,可能是小写、大写、首字母大写
  40. // 读取文件转换为全部的小写,然后在解析 xml ? etree 在转换为小写后,某些类型的文件的内容会崩溃···
  41. // 所以这里很傻的方式解决
  42. err := doc.ReadFromFile(nfoFilePath)
  43. if err != nil {
  44. return imdbInfo, err
  45. }
  46. for _, t := range doc.FindElements("./" + rootKey +"/title") {
  47. imdbInfo.Title = t.Text()
  48. break
  49. }
  50. //---------------------------------------------------------------------
  51. for _, t := range doc.FindElements("./" + rootKey +"/imdb_id") {
  52. imdbInfo.ImdbId = t.Text()
  53. break
  54. }
  55. for _, t := range doc.FindElements("//uniqueid[@type='imdb']") {
  56. imdbInfo.ImdbId = t.Text()
  57. break
  58. }
  59. for _, t := range doc.FindElements("//uniqueid[@type='Imdb']") {
  60. imdbInfo.ImdbId = t.Text()
  61. break
  62. }
  63. for _, t := range doc.FindElements("//uniqueid[@type='IMDB']") {
  64. imdbInfo.ImdbId = t.Text()
  65. break
  66. }
  67. //---------------------------------------------------------------------
  68. for _, t := range doc.FindElements("./" + rootKey +"/year") {
  69. imdbInfo.Year = t.Text()
  70. break
  71. }
  72. //---------------------------------------------------------------------
  73. for _, t := range doc.FindElements("./" + rootKey + "/releasedate") {
  74. imdbInfo.ReleaseDate = t.Text()
  75. break
  76. }
  77. //---------------------------------------------------------------------
  78. for _, t := range doc.FindElements("./" + rootKey + "/premiered") {
  79. imdbInfo.ReleaseDate = t.Text()
  80. break
  81. }
  82. if imdbInfo.ImdbId != "" {
  83. return imdbInfo, nil
  84. }
  85. return imdbInfo, common2.CanNotFindIMDBID
  86. }
  87. func GetImdbInfo4Movie(movieFileFullPath string) (types.VideoIMDBInfo, error) {
  88. imdbInfo := types.VideoIMDBInfo{}
  89. // movie 当前的目录
  90. dirPth := filepath.Dir(movieFileFullPath)
  91. // 与 movie 文件名一致的 nfo 文件名称
  92. movieNfoFileName := filepath.Base(movieFileFullPath)
  93. movieNfoFileName = strings.ReplaceAll(movieNfoFileName, filepath.Ext(movieFileFullPath), suffixNameNfo)
  94. // movie.xml
  95. movieXmlFPath := ""
  96. // movieName.nfo 文件
  97. movieNameNfoFPath := ""
  98. // 通用的 *.nfo
  99. nfoFilePath := ""
  100. dir, err := ioutil.ReadDir(dirPth)
  101. if err != nil {
  102. return imdbInfo, err
  103. }
  104. for _, fi := range dir {
  105. if fi.IsDir() == true {
  106. continue
  107. }
  108. upperName := strings.ToLower(fi.Name())
  109. if upperName == MetadataMovieXml {
  110. // 找 movie.xml
  111. movieXmlFPath = path.Join(dirPth, fi.Name())
  112. break
  113. } else if upperName == movieNfoFileName {
  114. // movieName.nfo 文件
  115. movieNameNfoFPath = path.Join(dirPth, fi.Name())
  116. break
  117. } else {
  118. // 找 *.nfo,很可能是 movie.nfo
  119. ok := strings.HasSuffix(fi.Name(), suffixNameNfo)
  120. if ok {
  121. nfoFilePath = path.Join(dirPth, fi.Name())
  122. }
  123. }
  124. }
  125. // 根据找到的开始解析
  126. if movieNameNfoFPath == "" && movieXmlFPath == "" && nfoFilePath == "" {
  127. return imdbInfo, common2.NoMetadataFile
  128. }
  129. // 优先分析 movieName.nfo 文件
  130. if movieNameNfoFPath != "" {
  131. imdbInfo, err = getImdbAndYearNfo(movieNameNfoFPath, "movie")
  132. if err != nil {
  133. return types.VideoIMDBInfo{}, err
  134. }
  135. return imdbInfo, nil
  136. }
  137. if movieXmlFPath != "" {
  138. imdbInfo, err = getImdbAndYearMovieXml(movieXmlFPath)
  139. if err != nil {
  140. GetLogger().Errorln("getImdbAndYearMovieXml error, move on:", err)
  141. } else {
  142. return imdbInfo, nil
  143. }
  144. }
  145. if nfoFilePath != "" {
  146. imdbInfo, err = getImdbAndYearNfo(nfoFilePath, "movie")
  147. if err != nil {
  148. return imdbInfo, err
  149. } else {
  150. return imdbInfo, nil
  151. }
  152. }
  153. return imdbInfo, common2.CanNotFindIMDBID
  154. }
  155. func GetImdbInfo4SeriesDir(seriesDir string) (types.VideoIMDBInfo, error) {
  156. imdbInfo := types.VideoIMDBInfo{}
  157. dir, err := ioutil.ReadDir(seriesDir)
  158. if err != nil {
  159. return imdbInfo, err
  160. }
  161. nfoFilePath := ""
  162. for _, fi := range dir {
  163. if fi.IsDir() == true {
  164. continue
  165. }
  166. upperName := strings.ToUpper(fi.Name())
  167. if upperName == strings.ToUpper(MetadateTVNfo) {
  168. // 连续剧的 nfo 文件
  169. nfoFilePath = path.Join(seriesDir, fi.Name())
  170. break
  171. } else {
  172. // 找 *.nfo
  173. ok := strings.HasSuffix(fi.Name(), suffixNameNfo)
  174. if ok {
  175. nfoFilePath = path.Join(seriesDir, fi.Name())
  176. }
  177. }
  178. }
  179. // 根据找到的开始解析
  180. if nfoFilePath == "" {
  181. return imdbInfo, common2.NoMetadataFile
  182. }
  183. imdbInfo, err = getImdbAndYearNfo(nfoFilePath, "tvshow")
  184. if err != nil {
  185. return types.VideoIMDBInfo{}, err
  186. }
  187. return imdbInfo, nil
  188. }
  189. func GetImdbInfo4OneSeriesEpisode(oneEpFPath string) (types.VideoIMDBInfo, error) {
  190. // 从这一集的视频文件全路径去推算对应的 nfo 文件是否存在
  191. EPdir := filepath.Dir(oneEpFPath)
  192. // 与 EP 文件名一致的 nfo 文件名称
  193. EpNfoFileName := filepath.Base(oneEpFPath)
  194. EpNfoFileName = strings.ReplaceAll(EpNfoFileName, filepath.Ext(oneEpFPath), suffixNameNfo)
  195. // 全路径
  196. EpNfoFPath := path.Join(EPdir, EpNfoFileName)
  197. //
  198. imdbInfo := types.VideoIMDBInfo{}
  199. doc := etree.NewDocument()
  200. // 这里会遇到一个梗,下面的关键词,可能是小写、大写、首字母大写
  201. // 读取文件转换为全部的小写,然后在解析 xml ? etree 在转换为小写后,某些类型的文件的内容会崩溃···
  202. // 所以这里很傻的方式解决
  203. err := doc.ReadFromFile(EpNfoFPath)
  204. if err != nil {
  205. return imdbInfo, err
  206. }
  207. for _, t := range doc.FindElements("./episodedetails/aired") {
  208. imdbInfo.ReleaseDate = t.Text()
  209. break
  210. }
  211. for _, t := range doc.FindElements("./episodedetails/premiered") {
  212. imdbInfo.ReleaseDate = t.Text()
  213. break
  214. }
  215. if imdbInfo.ReleaseDate != "" {
  216. return imdbInfo, nil
  217. }
  218. return imdbInfo, common2.CanNotFindEpAiredTime
  219. }
  220. // GetVideoInfoFromFileName 从文件名推断文件信息
  221. func GetVideoInfoFromFileName(fileName string) (*PTN.TorrentInfo, error) {
  222. parse, err := PTN.Parse(fileName)
  223. if err != nil {
  224. return nil, err
  225. }
  226. compile, err := regexp.Compile(regFixTitle2)
  227. if err != nil {
  228. return nil, err
  229. }
  230. match := compile.ReplaceAllString(parse.Title, "")
  231. match = strings.TrimRight(match, "")
  232. parse.Title = match
  233. return parse, nil
  234. }
  235. //GetVideoInfoFromFileFullPath 从全文件路径推断文件信息
  236. func GetVideoInfoFromFileFullPath(videoFileFullPath string) (*PTN.TorrentInfo, time.Time, error) {
  237. parse, err := PTN.Parse(filepath.Base(videoFileFullPath))
  238. if err != nil {
  239. return nil, time.Time{}, err
  240. }
  241. compile, err := regexp.Compile(regFixTitle2)
  242. if err != nil {
  243. return nil, time.Time{}, err
  244. }
  245. match := compile.ReplaceAllString(parse.Title, "")
  246. match = strings.TrimRight(match, "")
  247. parse.Title = match
  248. fInfo, err := os.Stat(videoFileFullPath)
  249. if err != nil {
  250. return nil, time.Time{}, err
  251. }
  252. return parse, fInfo.ModTime(), nil
  253. }
  254. // GetSeasonAndEpisodeFromSubFileName 从文件名推断 季 和 集 的信息 Season Episode
  255. func GetSeasonAndEpisodeFromSubFileName(videoFileName string) (bool, int, int, error) {
  256. upperName := strings.ToUpper(videoFileName)
  257. // 先进行单个 Episode 的匹配
  258. // Killing.Eve.S02E01.Do.You.Know.How
  259. var re = regexp.MustCompile(`(?m)\.S(\d+)E(\d+)\.`)
  260. matched := re.FindAllStringSubmatch(upperName, -1)
  261. if len(matched) < 1 {
  262. // Killing.Eve.S02.Do.You.Know.How
  263. // 看看是不是季度字幕打包
  264. re = regexp.MustCompile(`(?m)\.S(\d+)\.`)
  265. matched = re.FindAllStringSubmatch(upperName, -1)
  266. if len(matched) < 1 {
  267. return false, 0, 0, nil
  268. }
  269. season, err := GetNumber2int(matched[0][1])
  270. if err != nil {
  271. return false,0, 0, err
  272. }
  273. return true, season, 0, nil
  274. } else {
  275. // 一集的字幕
  276. season, err := GetNumber2int(matched[0][1])
  277. if err != nil {
  278. return false,0, 0, err
  279. }
  280. episode, err := GetNumber2int(matched[0][2])
  281. if err != nil {
  282. return false, 0, 0, err
  283. }
  284. return false, season, episode, nil
  285. }
  286. }
  287. func GetNumber2Float(input string) (float32, error) {
  288. compile := regexp.MustCompile(regGetNumber)
  289. params := compile.FindStringSubmatch(input)
  290. if len(params) == 0 {
  291. return 0, errors.New("get number not match")
  292. }
  293. fNum, err := strconv.ParseFloat(params[0],32)
  294. if err != nil {
  295. return 0, errors.New("get number ParseFloat error")
  296. }
  297. return float32(fNum), nil
  298. }
  299. func GetNumber2int(input string) (int, error) {
  300. compile := regexp.MustCompile(regGetNumber)
  301. params := compile.FindStringSubmatch(input)
  302. if len(params) == 0 {
  303. return 0, errors.New("get number not match")
  304. }
  305. fNum, err := strconv.Atoi(params[0])
  306. if err != nil {
  307. return 0, errors.New("get number ParseFloat error")
  308. }
  309. return fNum, nil
  310. }
  311. const (
  312. MetadataMovieXml = "movie.xml"
  313. suffixNameXml = ".xml"
  314. suffixNameNfo = ".nfo"
  315. MetadateTVNfo = "tvshow.nfo"
  316. // 去除特殊字符,仅仅之有中文
  317. regFixTitle = "[^\u4e00-\u9fa5a-zA-Z0-9\\s]"
  318. // 去除特殊字符,把特殊字符都写进去
  319. regFixTitle2 = "[`~!@#$%^&*()+-=|{}';'\\[\\].<>/?~!@#¥%……&*()——+|{}【】';”“’。、?]"
  320. // 获取数字
  321. regGetNumber = "(?:\\-)?\\d{1,}(?:\\.\\d{1,})?"
  322. )