xunlei.go 8.2 KB

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