shooter.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package shooter
  2. import (
  3. "crypto/md5"
  4. "errors"
  5. "fmt"
  6. "github.com/allanpk716/ChineseSubFinder/internal/logic/file_downloader"
  7. "github.com/allanpk716/ChineseSubFinder/internal/logic/task_queue"
  8. pkgcommon "github.com/allanpk716/ChineseSubFinder/internal/pkg/common"
  9. "github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
  10. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  11. "github.com/allanpk716/ChineseSubFinder/internal/pkg/notify_center"
  12. "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
  13. common2 "github.com/allanpk716/ChineseSubFinder/internal/types/common"
  14. "github.com/allanpk716/ChineseSubFinder/internal/types/language"
  15. "github.com/allanpk716/ChineseSubFinder/internal/types/series"
  16. "github.com/allanpk716/ChineseSubFinder/internal/types/supplier"
  17. "github.com/sirupsen/logrus"
  18. "math"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "time"
  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.getSubInfos(checkFileHash, checkFileName, qLan)
  47. if err != nil {
  48. s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Error", err)
  49. s.isAlive = false
  50. return false, 0
  51. }
  52. // 计算耗时
  53. s.isAlive = true
  54. return true, time.Since(startT).Milliseconds()
  55. }
  56. func (s *Supplier) IsAlive() bool {
  57. return s.isAlive
  58. }
  59. func (s *Supplier) OverDailyDownloadLimit() bool {
  60. // 对于这个接口暂时没有限制
  61. return false
  62. }
  63. func (s *Supplier) GetLogger() *logrus.Logger {
  64. return s.log
  65. }
  66. func (s *Supplier) GetSettings() *settings.Settings {
  67. return s.settings
  68. }
  69. func (s *Supplier) GetSupplierName() string {
  70. return common2.SubSiteShooter
  71. }
  72. func (s *Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
  73. return s.getSubListFromFile(filePath)
  74. }
  75. func (s *Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  76. return s.downloadSub4Series(seriesInfo)
  77. }
  78. func (s *Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  79. return s.downloadSub4Series(seriesInfo)
  80. }
  81. func (s *Supplier) getSubListFromFile(filePath string) ([]supplier.SubInfo, error) {
  82. defer func() {
  83. s.log.Debugln(s.GetSupplierName(), filePath, "End...")
  84. }()
  85. s.log.Debugln(s.GetSupplierName(), filePath, "Start...")
  86. // 可以提供的字幕查询 eng或者chn
  87. var outSubInfoList []supplier.SubInfo
  88. var jsonList []SublistShooter
  89. if my_util.IsFile(filePath) == false {
  90. // 这里传入的可能是蓝光结构的伪造存在的视频文件,需要检查一次这个文件是否存在
  91. bok, _, _ := decode.IsFakeBDMVWorked(filePath)
  92. if bok == false {
  93. nowError := errors.New(fmt.Sprintf("%s %s %s",
  94. s.GetSupplierName(),
  95. filePath,
  96. "not exist, and it`s not a Blue ray Video FakeFileName"))
  97. s.log.Errorln(nowError)
  98. return nil, nowError
  99. }
  100. }
  101. hash, err := ComputeFileHash(filePath)
  102. if err != nil {
  103. return nil, err
  104. }
  105. if hash == "" {
  106. return nil, common2.ShooterFileHashIsEmpty
  107. }
  108. fileName := filepath.Base(filePath)
  109. jsonList, err = s.getSubInfos(hash, fileName, qLan)
  110. if err != nil {
  111. return nil, err
  112. }
  113. for i, shooter := range jsonList {
  114. for _, file := range shooter.Files {
  115. subExt := file.Ext
  116. if strings.Contains(file.Ext, ".") == false {
  117. subExt = "." + subExt
  118. }
  119. subInfo, err := s.fileDownloader.Get(s.GetSupplierName(), int64(i), fileName, language.ChineseSimple, file.Link, 0, shooter.Delay)
  120. if err != nil {
  121. s.log.Error(err)
  122. continue
  123. }
  124. // 下载成功需要统计到今天的次数中
  125. _, err = task_queue.AddDailyDownloadCount(s.GetSupplierName(),
  126. my_util.GetPublicIP(s.settings.AdvancedSettings.TaskQueue, s.settings.AdvancedSettings.ProxySettings))
  127. if err != nil {
  128. s.log.Warningln(s.GetSupplierName(), "getSubListFromFile.AddDailyDownloadCount", err)
  129. }
  130. outSubInfoList = append(outSubInfoList, *subInfo)
  131. // 如果够了那么多个字幕就返回
  132. if len(outSubInfoList) >= s.topic {
  133. return outSubInfoList, nil
  134. }
  135. // 一层里面,下载一个文件就行了
  136. break
  137. }
  138. }
  139. return outSubInfoList, nil
  140. }
  141. func (s *Supplier) getSubInfos(fileHash, fileName, qLan string) ([]SublistShooter, error) {
  142. var jsonList []SublistShooter
  143. httpClient := my_util.NewHttpClient(s.settings.AdvancedSettings.ProxySettings)
  144. resp, err := httpClient.R().
  145. SetFormData(map[string]string{
  146. "filehash": fileHash,
  147. "pathinfo": fileName,
  148. "format": "json",
  149. "lang": qLan,
  150. }).
  151. SetResult(&jsonList).
  152. Post(s.settings.AdvancedSettings.SuppliersSettings.Shooter.RootUrl)
  153. if err != nil {
  154. if resp != nil {
  155. s.log.Errorln(s.GetSupplierName(), "NewHttpClient:", fileName, err.Error())
  156. notify_center.Notify.Add(s.GetSupplierName()+" NewHttpClient", fmt.Sprintf("filePath: %s, resp: %s, error: %s", fileName, resp.String(), err.Error()))
  157. }
  158. return nil, err
  159. }
  160. return jsonList, nil
  161. }
  162. func ComputeFileHash(filePath string) (string, error) {
  163. hash := ""
  164. fp, err := os.Open(filePath)
  165. if err != nil {
  166. return "", err
  167. }
  168. defer func() {
  169. _ = fp.Close()
  170. }()
  171. stat, err := fp.Stat()
  172. if err != nil {
  173. return "", err
  174. }
  175. size := float64(stat.Size())
  176. if size < 0xF000 {
  177. return "", common2.VideoFileIsTooSmall
  178. }
  179. samplePositions := [4]int64{
  180. 4 * 1024,
  181. int64(math.Floor(size / 3 * 2)),
  182. int64(math.Floor(size / 3)),
  183. int64(size - 8*1024)}
  184. var samples [4][]byte
  185. for i, position := range samplePositions {
  186. samples[i] = make([]byte, 4*1024)
  187. _, err = fp.ReadAt(samples[i], position)
  188. if err != nil {
  189. return "", err
  190. }
  191. }
  192. for _, sample := range samples {
  193. if len(hash) > 0 {
  194. hash += ";"
  195. }
  196. hash += fmt.Sprintf("%x", md5.Sum(sample))
  197. }
  198. return hash, nil
  199. }
  200. func (s *Supplier) downloadSub4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  201. var allSupplierSubInfo = make([]supplier.SubInfo, 0)
  202. index := 0
  203. // 这里拿到的 seriesInfo ,里面包含了,需要下载字幕的 Eps 信息
  204. for _, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
  205. index++
  206. pkgcommon.SetSubScanJobStatusScanSeriesSub(index, len(seriesInfo.NeedDlEpsKeyList),
  207. fmt.Sprintf("%v - S%v-E%v", episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode))
  208. one, err := s.getSubListFromFile(episodeInfo.FileFullPath)
  209. if err != nil {
  210. s.log.Errorln(s.GetSupplierName(), "getSubListFromFile", episodeInfo.FileFullPath)
  211. continue
  212. }
  213. if one == nil {
  214. // 没有搜索到字幕
  215. s.log.Infoln(s.GetSupplierName(), "Not Find Sub can be download",
  216. episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode)
  217. continue
  218. }
  219. // 需要赋值给字幕结构
  220. for i := range one {
  221. one[i].Season = episodeInfo.Season
  222. one[i].Episode = episodeInfo.Episode
  223. }
  224. allSupplierSubInfo = append(allSupplierSubInfo, one...)
  225. }
  226. // 返回前,需要把每一个 Eps 的 Season Episode 信息填充到每个 SubInfo 中
  227. return allSupplierSubInfo, nil
  228. }
  229. type FilesShooter struct {
  230. Ext string `json:"ext"`
  231. Link string `json:"link"`
  232. }
  233. type SublistShooter struct {
  234. Desc string `json:"desc"`
  235. Delay int64 `json:"delay"`
  236. Files []FilesShooter `json:"files"`
  237. }
  238. const (
  239. qLan = "Chn"
  240. checkFileHash = "234b0ff3685d6c46164b6b48cd39d69f;8be57624909f9d365dc81df43399d496;436de72e3c36a05a07875cc3249ae31a;237f498cfee89c67a22564e61047b053"
  241. checkFileName = "S05E09.mkv"
  242. )