xunlei.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package xunlei
  2. import (
  3. "crypto/sha1"
  4. "errors"
  5. "fmt"
  6. "math"
  7. "os"
  8. "path/filepath"
  9. "time"
  10. "github.com/allanpk716/ChineseSubFinder/pkg"
  11. "github.com/allanpk716/ChineseSubFinder/pkg/types/common"
  12. "github.com/allanpk716/ChineseSubFinder/pkg/types/series"
  13. "github.com/allanpk716/ChineseSubFinder/pkg/types/supplier"
  14. "github.com/allanpk716/ChineseSubFinder/pkg/decode"
  15. "github.com/allanpk716/ChineseSubFinder/pkg/language"
  16. "github.com/allanpk716/ChineseSubFinder/pkg/logic/file_downloader"
  17. "github.com/allanpk716/ChineseSubFinder/pkg/notify_center"
  18. "github.com/allanpk716/ChineseSubFinder/pkg/settings"
  19. "github.com/allanpk716/ChineseSubFinder/pkg/sub_parser_hub"
  20. "github.com/sirupsen/logrus"
  21. )
  22. type Supplier struct {
  23. log *logrus.Logger
  24. fileDownloader *file_downloader.FileDownloader
  25. topic int
  26. isAlive bool
  27. }
  28. func NewSupplier(fileDownloader *file_downloader.FileDownloader) *Supplier {
  29. sup := Supplier{}
  30. sup.log = fileDownloader.Log
  31. sup.fileDownloader = fileDownloader
  32. sup.topic = common.DownloadSubsPerSite
  33. sup.isAlive = true // 默认是可以使用的,如果 check 后,再调整状态
  34. if settings.Get().AdvancedSettings.Topic > 0 && settings.Get().AdvancedSettings.Topic != sup.topic {
  35. sup.topic = settings.Get().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) OverDailyDownloadLimit() bool {
  60. if settings.Get().AdvancedSettings.SuppliersSettings.Xunlei.DailyDownloadLimit == 0 {
  61. s.log.Warningln(s.GetSupplierName(), "DailyDownloadLimit is 0, will Skip Download")
  62. return true
  63. }
  64. // 对于这个接口暂时没有限制
  65. return false
  66. }
  67. func (s *Supplier) GetLogger() *logrus.Logger {
  68. return s.log
  69. }
  70. func (s *Supplier) GetSupplierName() string {
  71. return common.SubSiteXunLei
  72. }
  73. func (s *Supplier) GetSubListFromFile4Movie(filePath string) ([]supplier.SubInfo, error) {
  74. return s.getSubListFromFile(filePath)
  75. }
  76. func (s *Supplier) GetSubListFromFile4Series(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  77. return s.downloadSub4Series(seriesInfo)
  78. }
  79. func (s *Supplier) GetSubListFromFile4Anime(seriesInfo *series.SeriesInfo) ([]supplier.SubInfo, error) {
  80. return s.downloadSub4Series(seriesInfo)
  81. }
  82. func (s *Supplier) getSubListFromFile(filePath string) ([]supplier.SubInfo, error) {
  83. defer func() {
  84. s.log.Debugln(s.GetSupplierName(), filePath, "End...")
  85. }()
  86. s.log.Debugln(s.GetSupplierName(), filePath, "Start...")
  87. if pkg.IsFile(filePath) == false {
  88. // 这里传入的可能是蓝光结构的伪造存在的视频文件,需要检查一次这个文件是否存在
  89. bok, _, _ := decode.IsFakeBDMVWorked(filePath)
  90. if bok == false {
  91. nowError := errors.New(fmt.Sprintf("%s %s %s",
  92. s.GetSupplierName(),
  93. filePath,
  94. "not exist, and it`s not a Blue ray Video FakeFileName"))
  95. s.log.Errorln(nowError)
  96. return nil, nowError
  97. }
  98. }
  99. cid, err := s.getCid(filePath)
  100. var jsonList SublistSliceXunLei
  101. var tmpXunLeiSubListChinese = make([]SublistXunLei, 0)
  102. var outSubList []supplier.SubInfo
  103. if len(cid) == 0 {
  104. return nil, common.XunLeiCIdIsEmpty
  105. }
  106. jsonList, err = s.getSubInfos(filePath, cid)
  107. if err != nil {
  108. return nil, err
  109. }
  110. // 剔除空的
  111. for _, v := range jsonList.Sublist {
  112. if len(v.Scid) > 0 && v.Scid != "" {
  113. // 符合中文语言的先加入列表
  114. tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  115. if language.HasChineseLang(tmpLang) == true && sub_parser_hub.IsSubTypeWanted(v.Sname) == true {
  116. tmpXunLeiSubListChinese = append(tmpXunLeiSubListChinese, v)
  117. }
  118. }
  119. }
  120. // TODO 这里需要考虑,可以设置为高级选项,不够就用 unknow 来补充
  121. // 如果不够,再补 unknow
  122. if len(tmpXunLeiSubListChinese) < s.topic {
  123. for _, v := range jsonList.Sublist {
  124. if len(tmpXunLeiSubListChinese) >= s.topic {
  125. break
  126. }
  127. if len(v.Scid) > 0 && v.Scid != "" {
  128. tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  129. if language.HasChineseLang(tmpLang) == false {
  130. tmpXunLeiSubListChinese = append(tmpXunLeiSubListChinese, v)
  131. }
  132. }
  133. }
  134. }
  135. videoFileName := filepath.Base(filePath)
  136. // 再开始下载字幕
  137. for i, v := range tmpXunLeiSubListChinese {
  138. // 解析 xunlei 列表中的这个字幕类型转换到内部的字幕类型
  139. //tmpLang := language.LangConverter4Sub_Supplier(v.Language)
  140. subInfo, err := s.fileDownloader.Get(s.GetSupplierName(), int64(i), videoFileName, v.Surl, v.Svote, v.Roffset)
  141. if err != nil {
  142. s.log.Error("FileDownloader.Get", err)
  143. continue
  144. }
  145. outSubList = append(outSubList, *subInfo)
  146. }
  147. return outSubList, nil
  148. }
  149. func (s *Supplier) getSubInfos(filePath, cid string) (SublistSliceXunLei, error) {
  150. var jsonList SublistSliceXunLei
  151. httpClient, err := pkg.NewHttpClient()
  152. if err != nil {
  153. return jsonList, err
  154. }
  155. resp, err := httpClient.R().
  156. SetResult(&jsonList).
  157. Get(fmt.Sprintf(settings.Get().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. one, err := s.getSubListFromFile(episodeInfo.FileFullPath)
  210. if err != nil {
  211. s.log.Errorln(s.GetSupplierName(), "getSubListFromFile", episodeInfo.Season, episodeInfo.Episode,
  212. episodeInfo.FileFullPath)
  213. continue
  214. }
  215. if one == nil {
  216. // 没有搜索到字幕
  217. s.log.Infoln(s.GetSupplierName(), "Not Find Sub can be download",
  218. episodeInfo.Title, episodeInfo.Season, episodeInfo.Episode)
  219. continue
  220. }
  221. // 需要赋值给字幕结构
  222. for i := range one {
  223. one[i].Season = episodeInfo.Season
  224. one[i].Episode = episodeInfo.Episode
  225. }
  226. allSupplierSubInfo = append(allSupplierSubInfo, one...)
  227. }
  228. // 返回前,需要把每一个 Eps 的 Season Episode 信息填充到每个 SubInfo 中
  229. return allSupplierSubInfo, nil
  230. }
  231. type SublistXunLei struct {
  232. Scid string `json:"scid"`
  233. Sname string `json:"sname"`
  234. Language string `json:"language"`
  235. Rate string `json:"rate"`
  236. Surl string `json:"surl"`
  237. Svote int64 `json:"svote"`
  238. Roffset int64 `json:"roffset"`
  239. }
  240. type SublistSliceXunLei struct {
  241. Sublist []SublistXunLei
  242. }
  243. const (
  244. checkFileName = "CheckFileName"
  245. checkCID = "FB4E2AFF106112136DFC5ACC7339EB29D1EC0CF8"
  246. )