csf.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. package csf
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/allanpk716/ChineseSubFinder/internal/pkg/logic/file_downloader"
  12. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_folder"
  13. "github.com/allanpk716/ChineseSubFinder/internal/types/subparser"
  14. "github.com/allanpk716/ChineseSubFinder/internal/pkg/subtitle_best_api"
  15. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  16. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_file_hash"
  17. "github.com/allanpk716/ChineseSubFinder/internal/pkg/mix_media_info"
  18. "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
  19. common2 "github.com/allanpk716/ChineseSubFinder/internal/types/common"
  20. "github.com/allanpk716/ChineseSubFinder/internal/types/series"
  21. "github.com/allanpk716/ChineseSubFinder/internal/types/supplier"
  22. "github.com/sirupsen/logrus"
  23. )
  24. type Supplier struct {
  25. settings *settings.Settings
  26. log *logrus.Logger
  27. fileDownloader *file_downloader.FileDownloader
  28. topic int
  29. isAlive bool
  30. }
  31. func NewSupplier(fileDownloader *file_downloader.FileDownloader) *Supplier {
  32. sup := Supplier{}
  33. sup.log = fileDownloader.Log
  34. sup.fileDownloader = fileDownloader
  35. sup.topic = common2.DownloadSubsPerSite
  36. sup.isAlive = true // 默认是可以使用的,如果 check 后,再调整状态
  37. sup.settings = fileDownloader.Settings
  38. if sup.settings.AdvancedSettings.Topic > 0 && sup.settings.AdvancedSettings.Topic != sup.topic {
  39. sup.topic = sup.settings.AdvancedSettings.Topic
  40. }
  41. return &sup
  42. }
  43. func (s *Supplier) CheckAlive() (bool, int64) {
  44. // 计算当前时间
  45. startT := time.Now()
  46. _, err := s.fileDownloader.SubtitleBestApi.GetMediaInfo("tt4236770", "imdb", "series")
  47. if err != nil {
  48. s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Error", err)
  49. s.isAlive = false
  50. return false, 0
  51. }
  52. s.isAlive = true
  53. return true, time.Since(startT).Milliseconds()
  54. }
  55. func (s *Supplier) IsAlive() bool {
  56. return s.isAlive
  57. }
  58. func (s *Supplier) OverDailyDownloadLimit() bool {
  59. if s.settings.AdvancedSettings.SuppliersSettings.ChineseSubFinder.DailyDownloadLimit == 0 {
  60. s.log.Warningln(s.GetSupplierName(), "DailyDownloadLimit is 0, will Skip Download")
  61. return true
  62. }
  63. // 对于这个接口暂时没有限制
  64. return false
  65. }
  66. func (s *Supplier) GetLogger() *logrus.Logger {
  67. return s.log
  68. }
  69. func (s *Supplier) GetSettings() *settings.Settings {
  70. return s.settings
  71. }
  72. func (s *Supplier) GetSupplierName() string {
  73. return common2.SubSiteChineseSubFinder
  74. }
  75. func (s *Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
  76. outSubInfos := make([]supplier.SubInfo, 0)
  77. if s.settings.ExperimentalFunction.ShareSubSettings.ShareSubEnabled == false {
  78. return outSubInfos, nil
  79. }
  80. return s.findAndDownload(filePath, true, 0, 0)
  81. }
  82. func (s *Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  83. outSubInfos := make([]supplier.SubInfo, 0)
  84. if s.settings.ExperimentalFunction.ShareSubSettings.ShareSubEnabled == false {
  85. return outSubInfos, nil
  86. }
  87. // 这里拿到的 seriesInfo ,里面包含了,需要下载字幕的 Eps 信息
  88. for _, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
  89. oneSubInfoList, err := s.findAndDownload(episodeInfo.FileFullPath, false, episodeInfo.Season, episodeInfo.Episode)
  90. if err != nil {
  91. return outSubInfos, errors.New("FindAndDownload error:" + err.Error())
  92. }
  93. outSubInfos = append(outSubInfos, oneSubInfoList...)
  94. }
  95. return outSubInfos, nil
  96. }
  97. func (s *Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  98. outSubInfos := make([]supplier.SubInfo, 0)
  99. if s.settings.ExperimentalFunction.ShareSubSettings.ShareSubEnabled == false {
  100. return outSubInfos, nil
  101. }
  102. // 这里拿到的 seriesInfo ,里面包含了,需要下载字幕的 Eps 信息
  103. for _, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
  104. oneSubInfoList, err := s.findAndDownload(episodeInfo.FileFullPath, false, episodeInfo.Season, episodeInfo.Episode)
  105. if err != nil {
  106. return outSubInfos, errors.New("FindAndDownload error:" + err.Error())
  107. }
  108. outSubInfos = append(outSubInfos, oneSubInfoList...)
  109. }
  110. return outSubInfos, nil
  111. }
  112. func (s *Supplier) findAndDownload(videoFPath string, isMovie bool, Season, Episode int) (outSubInfoList []supplier.SubInfo, err error) {
  113. defer func() {
  114. s.log.Debugln(s.GetSupplierName(), videoFPath, "End...")
  115. }()
  116. s.log.Debugln(s.GetSupplierName(), videoFPath, "Start...")
  117. outSubInfoList = make([]supplier.SubInfo, 0)
  118. fileHash, err := sub_file_hash.Calculate(videoFPath)
  119. if err != nil {
  120. err = errors.New(fmt.Sprintf("%s.Calculate %s %s", s.GetSupplierName(), videoFPath, err))
  121. return
  122. }
  123. mediaInfo, err := mix_media_info.GetMixMediaInfo(s.log, s.fileDownloader.SubtitleBestApi, videoFPath, isMovie, s.settings.AdvancedSettings.ProxySettings)
  124. if err != nil {
  125. err = errors.New(fmt.Sprintf("%s.GetMixMediaInfo %s %s", s.GetSupplierName(), videoFPath, err))
  126. return
  127. }
  128. if isMovie == true {
  129. Season = 0
  130. Episode = 0
  131. }
  132. // 标记本次请求的归属性
  133. randomAuthToken := my_util.RandStringBytesMaskImprSrcSB(10)
  134. var bestOneSub subtitle_best_api.Subtitle
  135. var queueIsFull bool
  136. var waitTime int64
  137. reTryTimes := 0
  138. const maxRetryTimes = 5
  139. retryFail := false
  140. // 重试多次排队
  141. for {
  142. reTryTimes++
  143. if reTryTimes > maxRetryTimes {
  144. // 超过了最大重试次数,直接返回
  145. retryFail = true
  146. break
  147. }
  148. bestOneSub, queueIsFull, waitTime, err = s.askFindSubProcess(fileHash, mediaInfo.ImdbId, mediaInfo.TmdbId, Season, Episode, randomAuthToken, "")
  149. if err != nil {
  150. return nil, errors.New(fmt.Sprintf("askFindSubProcess Error: %s", err.Error()))
  151. }
  152. if queueIsFull == true {
  153. // 队列满了,需要等待再次重试
  154. time.Sleep(5 * time.Second)
  155. continue
  156. }
  157. // 没有错误,没有队列满,可以继续
  158. break
  159. }
  160. if retryFail == true {
  161. // 没有等待时间,直接返回
  162. s.log.Warningln("ask find queue is full, over max retry times, skip this time")
  163. return
  164. }
  165. if bestOneSub.SubSha256 != "" {
  166. // 说明查询的时候名中了缓存,无需往下继续查询,直接去排队下载即可
  167. } else {
  168. // 没有命中缓存,需要主动去查询
  169. // 等待一定的时间去查询
  170. if waitTime <= 0 {
  171. waitTime = 5
  172. }
  173. s.log.Infoln("will wait", waitTime, "s 2 ask find sub")
  174. // 等待耗时的动作
  175. var sleepCounter int64
  176. sleepCounter = 0
  177. for true {
  178. if sleepCounter > waitTime {
  179. break
  180. }
  181. if sleepCounter%30 == 0 {
  182. s.log.Infoln("wait 2 ask find sub")
  183. }
  184. time.Sleep(1 * time.Second)
  185. sleepCounter++
  186. }
  187. // 直接查询
  188. findSubReply, err := s.fileDownloader.SubtitleBestApi.FindSub(fileHash, mediaInfo.ImdbId, mediaInfo.TmdbId, strconv.Itoa(Season), strconv.Itoa(Episode), randomAuthToken, "")
  189. if err != nil {
  190. return nil, errors.New(fmt.Sprintf("FindSub Error: %s", err.Error()))
  191. }
  192. if len(findSubReply.Subtitle) < 1 {
  193. s.log.Warningln("FindSub Error: no sub found")
  194. return outSubInfoList, nil
  195. }
  196. bestOneSub = s.findBestSub(findSubReply.Subtitle)
  197. }
  198. tmpFolder, err := my_folder.GetRootTmpFolder()
  199. if err != nil {
  200. return nil, errors.New(fmt.Sprintf("GetRootTmpFolder Error: %s", err.Error()))
  201. }
  202. desSubSaveFPath := filepath.Join(tmpFolder, bestOneSub.SubSha256+bestOneSub.Ext)
  203. foundSubCache, cacheSubInfo, err := s.fileDownloader.GetCSF(bestOneSub.SubSha256)
  204. if err != nil {
  205. return nil, errors.New(fmt.Sprintf("GetCSF Error: %s", err.Error()))
  206. }
  207. if foundSubCache == true {
  208. // 在本地缓存中找到了
  209. cacheSubInfo.Season = Season
  210. cacheSubInfo.Episode = Episode
  211. outSubInfoList = append(outSubInfoList, *cacheSubInfo)
  212. return
  213. }
  214. // 需要从服务器拿去
  215. // 得到查询结果,去排队下载
  216. reTryTimes = 0
  217. retryFail = false
  218. // 重试多次排队
  219. for {
  220. reTryTimes++
  221. if reTryTimes > maxRetryTimes {
  222. // 超过了最大重试次数,直接返回
  223. retryFail = true
  224. break
  225. }
  226. queueIsFull, waitTime, err = s.askDownloadSubProcess(bestOneSub.SubSha256, randomAuthToken, "")
  227. if err != nil {
  228. err = errors.New(fmt.Sprintf("AskDownloadSub Error: %s", err.Error()))
  229. return
  230. }
  231. if queueIsFull == true {
  232. // 队列满了,需要等待再次重试
  233. time.Sleep(5 * time.Second)
  234. continue
  235. }
  236. // 没有错误,没有队列满,可以继续
  237. break
  238. }
  239. if retryFail == true {
  240. // 没有等待时间,直接返回
  241. s.log.Warningln("ask download queue is full, over max retry times, skip this time")
  242. return
  243. }
  244. // 等待一定的时间去查询
  245. if waitTime <= 0 {
  246. waitTime = 5
  247. }
  248. s.log.Infoln("will wait", waitTime, "s 2 ask download sub")
  249. // 等待耗时的动作
  250. var sleepCounter int64
  251. sleepCounter = 0
  252. for true {
  253. if sleepCounter > waitTime {
  254. break
  255. }
  256. if sleepCounter%30 == 0 {
  257. s.log.Infoln("wait 2 ask download sub")
  258. }
  259. time.Sleep(1 * time.Second)
  260. sleepCounter++
  261. }
  262. // 直接下载,这里需要再前面的过程中搞清楚这个字幕是什么后缀名,然后写入到缓存目录中
  263. downloadSubReply, err := s.fileDownloader.SubtitleBestApi.DownloadSub(bestOneSub.SubSha256, randomAuthToken, "", desSubSaveFPath)
  264. if err != nil {
  265. return nil, errors.New(fmt.Sprintf("DownloadSub Error: %s", err.Error()))
  266. }
  267. if downloadSubReply.Status == 0 {
  268. // 下载失败了
  269. err = errors.New(fmt.Sprintf("DownloadSub Error: %s", downloadSubReply.Message))
  270. return
  271. } else if downloadSubReply.Status == 1 {
  272. // 下载成功
  273. } else {
  274. // 不支持的返回值
  275. err = errors.New(fmt.Sprintf("DownloadSub Not Support Status: %d, Message: %s", downloadSubReply.Status, downloadSubReply.Message))
  276. return
  277. }
  278. // 下载成功后,需要判断一下这里的文件是否是字幕文件,如果不是,需要删除掉,然后跳过
  279. bok := false
  280. var subFileInfo *subparser.FileInfo
  281. bok, subFileInfo, err = s.fileDownloader.SubParserHub.DetermineFileTypeFromFile(desSubSaveFPath)
  282. if err != nil {
  283. return nil, errors.New(fmt.Sprintf("DetermineFileTypeFromFile Error: %s", err.Error()))
  284. }
  285. if bok == false {
  286. // 不是字幕文件,需要删除掉
  287. err = os.Remove(desSubSaveFPath)
  288. if err != nil {
  289. return nil, errors.New(fmt.Sprintf("Remove File %s Error: %s", desSubSaveFPath, err.Error()))
  290. }
  291. }
  292. subFileName := strings.ReplaceAll(filepath.Base(videoFPath), filepath.Ext(videoFPath), "")
  293. subBytes, err := ioutil.ReadFile(desSubSaveFPath)
  294. if err != nil {
  295. return nil, errors.New(fmt.Sprintf("ReadFile Error: %s", err.Error()))
  296. }
  297. oneSubInfo := supplier.NewSubInfo(s.GetSupplierName(), 1, subFileName, subFileInfo.Lang, bestOneSub.SubSha256, 0, 0, bestOneSub.Ext, subBytes)
  298. oneSubInfo.Season = Season
  299. oneSubInfo.Episode = Episode
  300. err = s.fileDownloader.AddCSF(oneSubInfo)
  301. if err != nil {
  302. return nil, errors.New(fmt.Sprintf("AddCSF Error: %s", err.Error()))
  303. }
  304. outSubInfoList = append(outSubInfoList, *oneSubInfo)
  305. return
  306. }
  307. // askFindSubProcess 查找字幕
  308. func (s *Supplier) askFindSubProcess(VideoFeature, ImdbId, TmdbId string, Season, Episode int, FindSubToken, ApiKey string) (bestOneSub subtitle_best_api.Subtitle, queueIsFull bool, waitTime int64, err error) {
  309. // 开始排队查询
  310. askFindSubReply, err := s.fileDownloader.SubtitleBestApi.AskFindSub(VideoFeature, ImdbId, TmdbId, strconv.Itoa(Season), strconv.Itoa(Episode), FindSubToken, ApiKey)
  311. if err != nil {
  312. err = errors.New(fmt.Sprintf("AskFindSub Error: %s", err.Error()))
  313. return
  314. }
  315. if askFindSubReply.Status == 0 {
  316. err = errors.New(fmt.Sprintf("AskFindSub Error: %s", askFindSubReply.Message))
  317. return
  318. } else if askFindSubReply.Status == 1 {
  319. // 成功,查询到了字幕列表(缓存有的)
  320. if len(askFindSubReply.Subtitle) < 1 {
  321. // 查询到了,但是返回的列表是空的,也直接返回
  322. return
  323. }
  324. // 那么获取到了字幕列表,接下来就要去排队请求这个字幕
  325. /*
  326. 每个返回的字幕中有两个特别的字段
  327. 1. match_video_feature,是否匹配传入的视频特征
  328. 2. low_trust,是否是低可信
  329. */
  330. bestOneSub = s.findBestSub(askFindSubReply.Subtitle)
  331. return
  332. } else if askFindSubReply.Status == 2 {
  333. // 放入队列,或者已经在队列中了,根据服务器安排的时间去请求排队下载
  334. // 得到目标时间与当前时间的差值,单位是s
  335. waitTime = askFindSubReply.ScheduledUnixTime - time.Now().Unix()
  336. return
  337. } else if askFindSubReply.Status == 3 {
  338. // 查询的队列满了
  339. queueIsFull = true
  340. return
  341. } else {
  342. // 不支持的返回值
  343. err = errors.New(fmt.Sprintf("AskFindSub Not Support Status: %d, Message: %s", askFindSubReply.Status, askFindSubReply.Message))
  344. return
  345. }
  346. }
  347. // askDownloadSubProcess 下载排队
  348. func (s *Supplier) askDownloadSubProcess(SubSha256, DownloadSubToken, ApiKey string) (queueIsFull bool, waitTime int64, err error) {
  349. askDownloadSubReply, err := s.fileDownloader.SubtitleBestApi.AskDownloadSub(SubSha256, DownloadSubToken, ApiKey)
  350. if err != nil {
  351. err = errors.New(fmt.Sprintf("AskDownloadSub Error: %s", err.Error()))
  352. return
  353. }
  354. if askDownloadSubReply.Status == 0 {
  355. err = errors.New(fmt.Sprintf("AskDownloadSub Error: %s", askDownloadSubReply.Message))
  356. return
  357. } else if askDownloadSubReply.Status == 2 {
  358. // 放入队列,或者已在队列中,根据等待的时间再去下载即可
  359. waitTime = askDownloadSubReply.ScheduledUnixTime - time.Now().Unix()
  360. return
  361. } else if askDownloadSubReply.Status == 3 {
  362. // 队列满了
  363. queueIsFull = true
  364. return
  365. } else {
  366. // 不应该出现 status = 1 的情况,这个先不改了,懒
  367. // 其他情况也是
  368. // 不支持的返回值
  369. err = errors.New(fmt.Sprintf("AskFindSub Not Support Status: %d, Message: %s", askDownloadSubReply.Status, askDownloadSubReply.Message))
  370. return
  371. }
  372. }
  373. func (s *Supplier) findBestSub(subtitles []subtitle_best_api.Subtitle) (bestOneSub subtitle_best_api.Subtitle) {
  374. found := false
  375. for _, subtitle := range subtitles {
  376. if subtitle.MatchVideoFeature == true && subtitle.LowTrust == false {
  377. // 匹配视频 且 高可信
  378. bestOneSub = subtitle
  379. found = true
  380. s.log.Infoln("Find Best Subtitle, MatchVideoFeature == true and HighTrust", subtitle.SubSha256)
  381. break
  382. }
  383. }
  384. if found == false {
  385. // 没有找到,那么就按照高可信来查询
  386. for _, subtitle := range subtitles {
  387. if subtitle.LowTrust == false {
  388. // 高可信
  389. bestOneSub = subtitle
  390. found = true
  391. s.log.Infoln("Find Best Subtitle, HighTrust", subtitle.SubSha256)
  392. break
  393. }
  394. }
  395. }
  396. if found == false {
  397. // 没有找到,那么就按照高视频匹配的来查询
  398. for _, subtitle := range subtitles {
  399. if subtitle.MatchVideoFeature == false {
  400. // 匹配视频
  401. bestOneSub = subtitle
  402. s.log.Infoln("Find Best Subtitle, MatchVideoFeature == true", subtitle.SubSha256)
  403. found = true
  404. break
  405. }
  406. }
  407. }
  408. if found == false {
  409. // 上面的都没触发,那么就返回第一个字幕吧
  410. bestOneSub = subtitles[0]
  411. s.log.Infoln("Find Best Subtitle, LowTrust", bestOneSub.SubSha256)
  412. }
  413. return
  414. }