xunlei.go 7.3 KB

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