shooter.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package shooter
  2. import (
  3. "crypto/md5"
  4. "fmt"
  5. "github.com/allanpk716/ChineseSubFinder/internal/common"
  6. pkgcommon "github.com/allanpk716/ChineseSubFinder/internal/pkg/common"
  7. "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
  8. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  9. "github.com/allanpk716/ChineseSubFinder/internal/pkg/notify_center"
  10. "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
  11. "github.com/allanpk716/ChineseSubFinder/internal/types/language"
  12. "github.com/allanpk716/ChineseSubFinder/internal/types/series"
  13. "github.com/allanpk716/ChineseSubFinder/internal/types/supplier"
  14. "github.com/huandu/go-clone"
  15. "github.com/sirupsen/logrus"
  16. "math"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "time"
  21. )
  22. type Supplier struct {
  23. settings settings.Settings
  24. log *logrus.Logger
  25. topic int
  26. }
  27. func NewSupplier(_settings settings.Settings) *Supplier {
  28. sup := Supplier{}
  29. sup.log = log_helper.GetLogger()
  30. sup.topic = common.DownloadSubsPerSite
  31. sup.settings = clone.Clone(_settings).(settings.Settings)
  32. if sup.settings.AdvancedSettings.Topic > 0 && sup.settings.AdvancedSettings.Topic != sup.topic {
  33. sup.topic = sup.settings.AdvancedSettings.Topic
  34. }
  35. return &sup
  36. }
  37. func (s Supplier) CheckAlive() (bool, int64) {
  38. // 计算当前时间
  39. startT := time.Now()
  40. _, err := s.getSubInfos(checkFileHash, checkFileName, qLan)
  41. if err != nil {
  42. s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Error", err)
  43. return false, 0
  44. }
  45. // 计算耗时
  46. return true, time.Since(startT).Milliseconds()
  47. }
  48. func (s Supplier) GetSupplierName() string {
  49. return common.SubSiteShooter
  50. }
  51. func (s Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
  52. return s.getSubListFromFile(filePath)
  53. }
  54. func (s Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  55. return s.downloadSub4Series(seriesInfo)
  56. }
  57. func (s Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  58. return s.downloadSub4Series(seriesInfo)
  59. }
  60. func (s Supplier) getSubListFromFile(filePath string) ([]supplier.SubInfo, error) {
  61. defer func() {
  62. s.log.Debugln(s.GetSupplierName(), filePath, "End...")
  63. }()
  64. s.log.Debugln(s.GetSupplierName(), filePath, "Start...")
  65. // 可以提供的字幕查询 eng或者chn
  66. var outSubInfoList []supplier.SubInfo
  67. var jsonList []SublistShooter
  68. hash, err := ComputeFileHash(filePath)
  69. if err != nil {
  70. return nil, err
  71. }
  72. if hash == "" {
  73. return nil, common.ShooterFileHashIsEmpty
  74. }
  75. fileName := filepath.Base(filePath)
  76. jsonList, err = s.getSubInfos(hash, fileName, qLan)
  77. if err != nil {
  78. return nil, err
  79. }
  80. for i, shooter := range jsonList {
  81. for _, file := range shooter.Files {
  82. subExt := file.Ext
  83. if strings.Contains(file.Ext, ".") == false {
  84. subExt = "." + subExt
  85. }
  86. data, _, err := my_util.DownFile(file.Link)
  87. if err != nil {
  88. s.log.Error(err)
  89. continue
  90. }
  91. onSub := supplier.NewSubInfo(s.GetSupplierName(), int64(i), fileName, language.ChineseSimple, file.Link, 0, shooter.Delay, subExt, data)
  92. outSubInfoList = append(outSubInfoList, *onSub)
  93. // 如果够了那么多个字幕就返回
  94. if len(outSubInfoList) >= s.topic {
  95. return outSubInfoList, nil
  96. }
  97. // 一层里面,下载一个文件就行了
  98. break
  99. }
  100. }
  101. return outSubInfoList, nil
  102. }
  103. func (s Supplier) getSubInfos(fileHash, fileName, qLan string) ([]SublistShooter, error) {
  104. var jsonList []SublistShooter
  105. httpClient := my_util.NewHttpClient(*s.settings.AdvancedSettings.ProxySettings)
  106. resp, err := httpClient.R().
  107. SetFormData(map[string]string{
  108. "filehash": fileHash,
  109. "pathinfo": fileName,
  110. "format": "json",
  111. "lang": qLan,
  112. }).
  113. SetResult(&jsonList).
  114. Post(common.SubShooterRootUrl)
  115. if err != nil {
  116. if resp != nil {
  117. s.log.Errorln(s.GetSupplierName(), "NewHttpClient:", fileName, err.Error())
  118. notify_center.Notify.Add(s.GetSupplierName()+" NewHttpClient", fmt.Sprintf("filePath: %s, resp: %s, error: %s", fileName, resp.String(), err.Error()))
  119. }
  120. return nil, err
  121. }
  122. return jsonList, nil
  123. }
  124. func ComputeFileHash(filePath string) (string, error) {
  125. hash := ""
  126. fp, err := os.Open(filePath)
  127. if err != nil {
  128. return "", err
  129. }
  130. defer func() {
  131. _ = fp.Close()
  132. }()
  133. stat, err := fp.Stat()
  134. if err != nil {
  135. return "", err
  136. }
  137. size := float64(stat.Size())
  138. if size < 0xF000 {
  139. return "", common.VideoFileIsTooSmall
  140. }
  141. samplePositions := [4]int64{
  142. 4 * 1024,
  143. int64(math.Floor(size / 3 * 2)),
  144. int64(math.Floor(size / 3)),
  145. int64(size - 8*1024)}
  146. var samples [4][]byte
  147. for i, position := range samplePositions {
  148. samples[i] = make([]byte, 4*1024)
  149. _, err = fp.ReadAt(samples[i], position)
  150. if err != nil {
  151. return "", err
  152. }
  153. }
  154. for _, sample := range samples {
  155. if len(hash) > 0 {
  156. hash += ";"
  157. }
  158. hash += fmt.Sprintf("%x", md5.Sum(sample))
  159. }
  160. return hash, nil
  161. }
  162. func (s Supplier) downloadSub4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  163. var allSupplierSubInfo = make([]supplier.SubInfo, 0)
  164. index := 0
  165. // 这里拿到的 seriesInfo ,里面包含了,需要下载字幕的 Eps 信息
  166. for _, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
  167. index++
  168. pkgcommon.SetSubScanJobStatusScanSeriesSub(index, len(seriesInfo.NeedDlEpsKeyList),
  169. fmt.Sprintf("%v - S%v-E%v", episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode))
  170. one, err := s.getSubListFromFile(episodeInfo.FileFullPath)
  171. if err != nil {
  172. s.log.Errorln(s.GetSupplierName(), "getSubListFromFile", episodeInfo.FileFullPath)
  173. continue
  174. }
  175. if one == nil {
  176. // 没有搜索到字幕
  177. s.log.Infoln(s.GetSupplierName(), "Not Find Sub can be download",
  178. episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode)
  179. continue
  180. }
  181. // 需要赋值给字幕结构
  182. for i := range one {
  183. one[i].Season = episodeInfo.Season
  184. one[i].Episode = episodeInfo.Episode
  185. }
  186. allSupplierSubInfo = append(allSupplierSubInfo, one...)
  187. }
  188. // 返回前,需要把每一个 Eps 的 Season Episode 信息填充到每个 SubInfo 中
  189. return allSupplierSubInfo, nil
  190. }
  191. type FilesShooter struct {
  192. Ext string `json:"ext"`
  193. Link string `json:"link"`
  194. }
  195. type SublistShooter struct {
  196. Desc string `json:"desc"`
  197. Delay int64 `json:"delay"`
  198. Files []FilesShooter `json:"files"`
  199. }
  200. const (
  201. qLan = "Chn"
  202. checkFileHash = "234b0ff3685d6c46164b6b48cd39d69f;8be57624909f9d365dc81df43399d496;436de72e3c36a05a07875cc3249ae31a;237f498cfee89c67a22564e61047b053"
  203. checkFileName = "S05E09.mkv"
  204. )