search.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. package search
  2. import (
  3. "github.com/ChineseSubFinder/ChineseSubFinder/internal/models"
  4. "io/fs"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "github.com/ChineseSubFinder/ChineseSubFinder/pkg"
  10. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/backend"
  11. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/sub_parser_hub"
  12. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/decode"
  13. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/filter"
  14. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/sort_things"
  15. "github.com/emirpasic/gods/maps/treemap"
  16. "github.com/sirupsen/logrus"
  17. )
  18. // VideoNameSearchKeywordMaker 拼接视频搜索的 title 和 年份
  19. func VideoNameSearchKeywordMaker(l *logrus.Logger, title string, year string) string {
  20. iYear, err := strconv.Atoi(year)
  21. if err != nil {
  22. // 允许的错误
  23. l.Errorln("VideoNameSearchKeywordMaker", "year to int", err)
  24. iYear = 0
  25. }
  26. searchKeyword := title
  27. if iYear >= 2020 {
  28. searchKeyword = searchKeyword + " " + year
  29. }
  30. return searchKeyword
  31. }
  32. // MatchedVideoFileFromDirs 搜索符合后缀名的视频文件
  33. func MatchedVideoFileFromDirs(l *logrus.Logger, dirs []string) (*treemap.Map, error) {
  34. defer func() {
  35. l.Infoln("MatchedVideoFileFromDirs End")
  36. l.Infoln(" --------------------------------------------------")
  37. }()
  38. l.Infoln(" --------------------------------------------------")
  39. l.Infoln("MatchedVideoFileFromDirs Start...")
  40. var fileFullPathMap = treemap.NewWithStringComparator()
  41. for _, dir := range dirs {
  42. matchedVideoFile, err := MatchedVideoFile(l, dir)
  43. if err != nil {
  44. return nil, err
  45. }
  46. value, found := fileFullPathMap.Get(dir)
  47. if found == false {
  48. fileFullPathMap.Put(dir, matchedVideoFile)
  49. } else {
  50. value = append(value.([]string), matchedVideoFile...)
  51. fileFullPathMap.Put(dir, value)
  52. }
  53. }
  54. fileFullPathMap.Each(func(seriesRootPathName interface{}, seriesNames interface{}) {
  55. oneSeriesRootPathName := seriesRootPathName.(string)
  56. fileFullPathList := seriesNames.([]string)
  57. // 排序,从最新的到最早的
  58. fileFullPathList = sort_things.SortByModTime(fileFullPathList)
  59. for _, s := range fileFullPathList {
  60. l.Debugln(s)
  61. }
  62. fileFullPathMap.Put(oneSeriesRootPathName, fileFullPathList)
  63. })
  64. return fileFullPathMap, nil
  65. }
  66. // MatchedVideoFile 搜索符合后缀名的视频文件,现在也会把 BDMV 的文件搜索出来,但是这个并不是一个视频文件,需要在后续特殊处理
  67. func MatchedVideoFile(l *logrus.Logger, dir string) ([]string, error) {
  68. var fileFullPathList = make([]string, 0)
  69. pathSep := string(os.PathSeparator)
  70. files, err := os.ReadDir(dir)
  71. if err != nil {
  72. return nil, err
  73. }
  74. for _, curFile := range files {
  75. fullPath := dir + pathSep + curFile.Name()
  76. if curFile.IsDir() {
  77. // 内层的错误就无视了
  78. oneList, _ := MatchedVideoFile(l, fullPath)
  79. if oneList != nil {
  80. fileFullPathList = append(fileFullPathList, oneList...)
  81. }
  82. } else {
  83. // 这里就是文件了
  84. bok, fakeBDMVVideoFile := pkg.FileNameIsBDMV(fullPath)
  85. if bok == true {
  86. // 这类文件后续的扫描字幕操作需要额外的处理
  87. fileFullPathList = append(fileFullPathList, fakeBDMVVideoFile)
  88. continue
  89. }
  90. if pkg.IsWantedVideoExtDef(curFile.Name()) == false {
  91. // 不是期望的视频后缀名则跳过
  92. continue
  93. } else {
  94. // 这里还有一种情况,就是蓝光, BDMV 下面会有一个 STREAM 文件夹,里面很多 m2ts 的视频组成
  95. if filepath.Base(filepath.Dir(fullPath)) == "STREAM" {
  96. l.Debugln("MatchedVideoFile, Skip BDMV.STREAM:", fullPath)
  97. continue
  98. }
  99. if filter.SkipFileInfo(l, curFile, fullPath) == true {
  100. continue
  101. }
  102. fileFullPathList = append(fileFullPathList, fullPath)
  103. }
  104. }
  105. }
  106. return fileFullPathList, nil
  107. }
  108. func TVNfo(l *logrus.Logger, dir string) ([]string, error) {
  109. var fileFullPathList = make([]string, 0)
  110. pathSep := string(os.PathSeparator)
  111. files, err := os.ReadDir(dir)
  112. if err != nil {
  113. return nil, err
  114. }
  115. for _, curFile := range files {
  116. fullPath := dir + pathSep + curFile.Name()
  117. if curFile.IsDir() {
  118. // 内层的错误就无视了
  119. oneList, _ := TVNfo(l, fullPath)
  120. if oneList != nil {
  121. fileFullPathList = append(fileFullPathList, oneList...)
  122. }
  123. } else {
  124. // 这里就是文件了
  125. if strings.ToLower(curFile.Name()) != decode.MetadateTVNfo {
  126. continue
  127. } else {
  128. //if filter.SkipFileInfo(l, curFile, fullPath) == true {
  129. // continue
  130. //}
  131. fileFullPathList = append(fileFullPathList, fullPath)
  132. }
  133. }
  134. }
  135. return fileFullPathList, nil
  136. }
  137. // SeriesAllEpsAndSubtitles 遍历这个连续剧目录下的所有视频文件,以及这个视频文件对应的字幕文件,这里无法转换给出静态文件服务器的路径,需要额外再对回去到的信息进行处理
  138. func SeriesAllEpsAndSubtitles(l *logrus.Logger, dir string) (*backend.SeasonInfo, error) {
  139. seasonInfo := backend.SeasonInfo{
  140. Name: filepath.Base(dir),
  141. RootDirPath: dir,
  142. OneVideoInfos: make([]backend.OneVideoInfo, 0),
  143. }
  144. pathVideoMap := make(map[string][]string, 0)
  145. pathSubsMap := make(map[string][]string, 0)
  146. err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
  147. if err != nil {
  148. return err
  149. }
  150. if d.IsDir() == true {
  151. // 跳过文件夹
  152. return nil
  153. }
  154. if filter.SkipFileInfo(l, d, path) == true {
  155. return nil
  156. }
  157. if pkg.IsWantedVideoExtDef(d.Name()) == true {
  158. // 如果是符合视频的后缀名,那么就缓存起来
  159. tmpDir := filepath.Dir(path)
  160. _, found := pathVideoMap[tmpDir]
  161. if found == false {
  162. pathVideoMap[tmpDir] = make([]string, 0)
  163. }
  164. pathVideoMap[tmpDir] = append(pathVideoMap[tmpDir], path)
  165. return nil
  166. }
  167. if sub_parser_hub.IsSubExtWanted(d.Name()) == true {
  168. // 如果是符合字幕的后缀名,那么就缓存起来
  169. tmpDir := filepath.Dir(path)
  170. _, found := pathSubsMap[tmpDir]
  171. if found == false {
  172. pathSubsMap[tmpDir] = make([]string, 0)
  173. }
  174. pathSubsMap[tmpDir] = append(pathSubsMap[tmpDir], path)
  175. return nil
  176. }
  177. return nil
  178. })
  179. if err != nil {
  180. return nil, err
  181. }
  182. // 交叉比对,找到对应的字幕
  183. for pathKey, videos := range pathVideoMap {
  184. nowPathSubs, _ := pathSubsMap[pathKey]
  185. //if found == false {
  186. // // 没有找到对应的字幕
  187. // continue
  188. //}
  189. for _, oneVideo := range videos {
  190. videoName := strings.ReplaceAll(filepath.Base(oneVideo), filepath.Ext(oneVideo), "")
  191. skipInfo := models.NewSkipScanInfoBySeriesEx(oneVideo, true)
  192. if skipInfo.Season() == -1 || skipInfo.Eps() == -1 {
  193. // 无法解析的视频,跳过
  194. l.Errorln("SeriesAllEpsAndSubtitles, Skip UnParse Video:", oneVideo)
  195. continue
  196. }
  197. nowOneVideoInfo := backend.OneVideoInfo{
  198. Name: filepath.Base(oneVideo),
  199. VideoFPath: oneVideo,
  200. Season: skipInfo.Season(),
  201. Episode: skipInfo.Eps(),
  202. SubFPathList: make([]string, 0),
  203. SubUrlList: make([]string, 0),
  204. }
  205. // 解析这个视频的 SxxExx 信息
  206. for _, oneSub := range nowPathSubs {
  207. if strings.HasPrefix(filepath.Base(oneSub), videoName) == false {
  208. continue
  209. }
  210. // 找到了对应的字幕
  211. nowOneVideoInfo.SubFPathList = append(nowOneVideoInfo.SubFPathList, oneSub)
  212. }
  213. seasonInfo.OneVideoInfos = append(seasonInfo.OneVideoInfos, nowOneVideoInfo)
  214. }
  215. }
  216. return &seasonInfo, nil
  217. }