xunlei.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. package xunlei
  2. import (
  3. "crypto/sha1"
  4. "errors"
  5. "fmt"
  6. "github.com/allanpk716/ChineseSubFinder/internal/logic/task_queue"
  7. pkgcommon "github.com/allanpk716/ChineseSubFinder/internal/pkg/common"
  8. "github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
  9. "github.com/allanpk716/ChineseSubFinder/internal/pkg/language"
  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. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_parser_hub"
  14. common2 "github.com/allanpk716/ChineseSubFinder/internal/types/common"
  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. "time"
  22. )
  23. type Supplier struct {
  24. settings *settings.Settings
  25. log *logrus.Logger
  26. topic int
  27. isAlive bool
  28. }
  29. func NewSupplier(_settings *settings.Settings, _logger *logrus.Logger) *Supplier {
  30. sup := Supplier{}
  31. sup.log = _logger
  32. sup.topic = common2.DownloadSubsPerSite
  33. sup.isAlive = true // 默认是可以使用的,如果 check 后,再调整状态
  34. sup.settings = _settings
  35. if sup.settings.AdvancedSettings.Topic > 0 && sup.settings.AdvancedSettings.Topic != sup.topic {
  36. sup.topic = sup.settings.AdvancedSettings.Topic
  37. }
  38. return &sup
  39. }
  40. func (s *Supplier) CheckAlive() (bool, int64) {
  41. // 计算当前时间
  42. startT := time.Now()
  43. jsonList, err := s.getSubInfos(checkFileName, checkCID)
  44. if err != nil {
  45. s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Error", err)
  46. s.isAlive = false
  47. return false, 0
  48. }
  49. if len(jsonList.Sublist) < 1 {
  50. s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Sublist < 1")
  51. s.isAlive = false
  52. return false, 0
  53. }
  54. s.isAlive = true
  55. return true, time.Since(startT).Milliseconds()
  56. }
  57. func (s *Supplier) IsAlive() bool {
  58. return s.isAlive
  59. }
  60. func (s *Supplier) OverDailyDownloadLimit() bool {
  61. // 对于这个接口暂时没有限制
  62. return false
  63. }
  64. func (s *Supplier) GetSupplierName() string {
  65. return common2.SubSiteXunLei
  66. }
  67. func (s *Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
  68. return s.getSubListFromFile(filePath)
  69. }
  70. func (s *Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  71. return s.downloadSub4Series(seriesInfo)
  72. }
  73. func (s *Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  74. return s.downloadSub4Series(seriesInfo)
  75. }
  76. func (s *Supplier) getSubListFromFile(filePath string) ([]supplier.SubInfo, error) {
  77. defer func() {
  78. s.log.Debugln(s.GetSupplierName(), filePath, "End...")
  79. }()
  80. s.log.Debugln(s.GetSupplierName(), filePath, "Start...")
  81. if my_util.IsFile(filePath) == false {
  82. // 这里传入的可能是蓝光结构的伪造存在的视频文件,需要检查一次这个文件是否存在
  83. bok, _ := decode.IsFakeBDMVWorked(filePath)
  84. if bok == false {
  85. nowError := errors.New(fmt.Sprintf("%s %s %s",
  86. s.GetSupplierName(),
  87. filePath,
  88. "not exist, and it`s not a Blue ray Video FakeFileName"))
  89. s.log.Errorln(nowError)
  90. return nil, nowError
  91. }
  92. }
  93. cid, err := s.getCid(filePath)
  94. var jsonList SublistSliceXunLei
  95. var tmpXunLeiSubListChinese = make([]SublistXunLei, 0)
  96. var outSubList []supplier.SubInfo
  97. if len(cid) == 0 {
  98. return nil, common2.XunLeiCIdIsEmpty
  99. }
  100. jsonList, err = s.getSubInfos(filePath, cid)
  101. if err != nil {
  102. return nil, err
  103. }
  104. // 剔除空的
  105. for _, v := range jsonList.Sublist {
  106. if len(v.Scid) > 0 && v.Scid != "" {
  107. // 符合中文语言的先加入列表
  108. tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  109. if language.HasChineseLang(tmpLang) == true && sub_parser_hub.IsSubTypeWanted(v.Sname) == true {
  110. tmpXunLeiSubListChinese = append(tmpXunLeiSubListChinese, v)
  111. }
  112. }
  113. }
  114. // TODO 这里需要考虑,可以设置为高级选项,不够就用 unknow 来补充
  115. // 如果不够,再补 unknow
  116. if len(tmpXunLeiSubListChinese) < s.topic {
  117. for _, v := range jsonList.Sublist {
  118. if len(tmpXunLeiSubListChinese) >= s.topic {
  119. break
  120. }
  121. if len(v.Scid) > 0 && v.Scid != "" {
  122. tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  123. if language.HasChineseLang(tmpLang) == false {
  124. tmpXunLeiSubListChinese = append(tmpXunLeiSubListChinese, v)
  125. }
  126. }
  127. }
  128. }
  129. // 再开始下载字幕
  130. for i, v := range tmpXunLeiSubListChinese {
  131. tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  132. data, filename, err := my_util.DownFile(s.log, v.Surl)
  133. if err != nil {
  134. s.log.Errorln("xunlei pkg.DownFile:", err)
  135. continue
  136. }
  137. // 下载成功需要统计到今天的次数中
  138. _, err = task_queue.AddDailyDownloadCount(s.GetSupplierName())
  139. if err != nil {
  140. s.log.Warningln(s.GetSupplierName(), "getSubListFromFile.AddDailyDownloadCount", err)
  141. }
  142. ext := ""
  143. if filename == "" {
  144. ext = filepath.Ext(v.Surl)
  145. } else {
  146. ext = filepath.Ext(filename)
  147. }
  148. outSubList = append(outSubList, *supplier.NewSubInfo(s.GetSupplierName(), int64(i), v.Sname, tmpLang, v.Surl, v.Svote, v.Roffset, ext, data))
  149. }
  150. return outSubList, nil
  151. }
  152. func (s *Supplier) getSubInfos(filePath, cid string) (SublistSliceXunLei, error) {
  153. var jsonList SublistSliceXunLei
  154. httpClient := my_util.NewHttpClient(*s.settings.AdvancedSettings.ProxySettings)
  155. resp, err := httpClient.R().
  156. SetResult(&jsonList).
  157. Get(fmt.Sprintf(s.settings.AdvancedSettings.SuppliersSettings.Xunlei.RootUrl, cid))
  158. if err != nil {
  159. if resp != nil {
  160. s.log.Errorln(s.GetSupplierName(), "NewHttpClient:", filePath, err.Error())
  161. notify_center.Notify.Add(s.GetSupplierName()+" NewHttpClient", fmt.Sprintf("filePath: %s, resp: %s, error: %s", filePath, resp.String(), err.Error()))
  162. }
  163. return jsonList, err
  164. }
  165. return jsonList, nil
  166. }
  167. //getCid 获取指定文件的唯一 cid
  168. func (s *Supplier) getCid(filePath string) (string, error) {
  169. hash := ""
  170. sha1Ctx := sha1.New()
  171. fp, err := os.Open(filePath)
  172. if err != nil {
  173. return "", err
  174. }
  175. defer func() {
  176. _ = fp.Close()
  177. }()
  178. stat, err := fp.Stat()
  179. if err != nil {
  180. return "", err
  181. }
  182. fileLength := stat.Size()
  183. if fileLength < 0xF000 {
  184. return "", err
  185. }
  186. bufferSize := int64(0x5000)
  187. positions := []int64{0, int64(math.Floor(float64(fileLength) / 3)), fileLength - bufferSize}
  188. for _, position := range positions {
  189. var buffer = make([]byte, bufferSize)
  190. _, err = fp.Seek(position, 0)
  191. if err != nil {
  192. return "", err
  193. }
  194. _, err = fp.Read(buffer)
  195. if err != nil {
  196. return "", err
  197. }
  198. sha1Ctx.Write(buffer)
  199. }
  200. hash = fmt.Sprintf("%X", sha1Ctx.Sum(nil))
  201. return hash, nil
  202. }
  203. func (s *Supplier) downloadSub4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  204. var allSupplierSubInfo = make([]supplier.SubInfo, 0)
  205. index := 0
  206. // 这里拿到的 seriesInfo ,里面包含了,需要下载字幕的 Eps 信息
  207. for _, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
  208. index++
  209. pkgcommon.SetSubScanJobStatusScanSeriesSub(index, len(seriesInfo.NeedDlEpsKeyList),
  210. fmt.Sprintf("%v - S%v-E%v", episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode))
  211. one, err := s.getSubListFromFile(episodeInfo.FileFullPath)
  212. if err != nil {
  213. s.log.Errorln(s.GetSupplierName(), "getSubListFromFile", episodeInfo.Season, episodeInfo.Episode,
  214. episodeInfo.FileFullPath)
  215. continue
  216. }
  217. if one == nil {
  218. // 没有搜索到字幕
  219. s.log.Infoln(s.GetSupplierName(), "Not Find Sub can be download",
  220. episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode)
  221. continue
  222. }
  223. // 需要赋值给字幕结构
  224. for i := range one {
  225. one[i].Season = episodeInfo.Season
  226. one[i].Episode = episodeInfo.Episode
  227. }
  228. allSupplierSubInfo = append(allSupplierSubInfo, one...)
  229. }
  230. // 返回前,需要把每一个 Eps 的 Season Episode 信息填充到每个 SubInfo 中
  231. return allSupplierSubInfo, nil
  232. }
  233. type SublistXunLei struct {
  234. Scid string `json:"scid"`
  235. Sname string `json:"sname"`
  236. Language string `json:"language"`
  237. Rate string `json:"rate"`
  238. Surl string `json:"surl"`
  239. Svote int64 `json:"svote"`
  240. Roffset int64 `json:"roffset"`
  241. }
  242. type SublistSliceXunLei struct {
  243. Sublist []SublistXunLei
  244. }
  245. const (
  246. checkFileName = "CheckFileName"
  247. checkCID = "FB4E2AFF106112136DFC5ACC7339EB29D1EC0CF8"
  248. )