emby_api.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package emby_api
  2. import (
  3. "fmt"
  4. "net/http"
  5. "sync"
  6. "time"
  7. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/emby"
  8. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/settings"
  9. "github.com/go-resty/resty/v2"
  10. "github.com/panjf2000/ants/v2"
  11. "github.com/sirupsen/logrus"
  12. "golang.org/x/net/context"
  13. )
  14. type EmbyApi struct {
  15. log *logrus.Logger
  16. timeOut time.Duration
  17. }
  18. func NewEmbyApi(log *logrus.Logger) *EmbyApi {
  19. em := EmbyApi{}
  20. em.log = log
  21. // 检查是否超过范围
  22. settings.Get().Check()
  23. // 强制设置
  24. em.timeOut = 5 * 60 * time.Second
  25. return &em
  26. }
  27. // RefreshRecentlyVideoInfo 字幕下载完毕一次,就可以触发一次这个。并发 6 线程去刷新
  28. func (em *EmbyApi) RefreshRecentlyVideoInfo(embySettings *settings.EmbySettings, SkipWatched bool, maxRequestVideoNumber int) error {
  29. items, err := em.GetRecentlyItems(embySettings, SkipWatched, maxRequestVideoNumber)
  30. if err != nil {
  31. return err
  32. }
  33. em.log.Debugln("RefreshRecentlyVideoInfo - GetRecentlyItems Count", len(items.Items))
  34. updateFunc := func(i interface{}) error {
  35. tmpId := i.(string)
  36. return em.UpdateVideoSubList(embySettings, tmpId)
  37. }
  38. p, err := ants.NewPoolWithFunc(embySettings.Threads, func(inData interface{}) {
  39. data := inData.(InputData)
  40. defer data.Wg.Done()
  41. ctx, cancel := context.WithTimeout(context.Background(), em.timeOut)
  42. defer cancel()
  43. done := make(chan error, 1)
  44. panicChan := make(chan interface{}, 1)
  45. go func() {
  46. defer func() {
  47. if p := recover(); p != nil {
  48. panicChan <- p
  49. }
  50. close(done)
  51. close(panicChan)
  52. }()
  53. done <- updateFunc(data.Id)
  54. }()
  55. select {
  56. case err = <-done:
  57. if err != nil {
  58. em.log.Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got error", err)
  59. }
  60. return
  61. case p := <-panicChan:
  62. em.log.Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got panic", p)
  63. case <-ctx.Done():
  64. em.log.Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got time out", ctx.Err())
  65. return
  66. }
  67. })
  68. if err != nil {
  69. return err
  70. }
  71. defer p.Release()
  72. wg := sync.WaitGroup{}
  73. for _, item := range items.Items {
  74. wg.Add(1)
  75. err = p.Invoke(InputData{Id: item.Id, Wg: &wg})
  76. if err != nil {
  77. em.log.Errorln("RefreshRecentlyVideoInfo ants.Invoke", err)
  78. }
  79. }
  80. wg.Wait()
  81. return nil
  82. }
  83. func (em *EmbyApi) GetRecentItemsByUserID(embySettings *settings.EmbySettings, userId string, maxRequestVideoNumber int) (emby.EmbyRecentlyItems, error) {
  84. var tmpRecItems emby.EmbyRecentlyItems
  85. // 获取指定用户的视频列表
  86. _, err := em.createClient().R().
  87. SetQueryParams(map[string]string{
  88. "api_key": embySettings.APIKey,
  89. "IsUnaired": "false",
  90. "Limit": fmt.Sprintf("%d", maxRequestVideoNumber),
  91. "Recursive": "true",
  92. "SortOrder": "Descending",
  93. "IncludeItemTypes": "Episode,Movie",
  94. "Filters": "IsNotFolder",
  95. "SortBy": "DateCreated",
  96. }).
  97. SetResult(&tmpRecItems).
  98. Get(embySettings.AddressUrl + "/emby/Users/" + userId + "/Items")
  99. if err != nil {
  100. return emby.EmbyRecentlyItems{}, err
  101. }
  102. return tmpRecItems, nil
  103. }
  104. // GetRecentlyItems 获取近期的视频(根据 SkipWatched 的情况,如果不跳过,那么就是获取所有用户的列表,如果是跳过,那么就会单独读取每个用户的再交叉判断)
  105. // 在 API 调试界面 -- ItemsService
  106. func (em *EmbyApi) GetRecentlyItems(embySettings *settings.EmbySettings, SkipWatched bool, maxRequestVideoNumber int) (emby.EmbyRecentlyItems, error) {
  107. var recItems emby.EmbyRecentlyItems
  108. recItems.Items = make([]emby.EmbyRecentlyItem, 0)
  109. var recItemMap = make(map[string]emby.EmbyRecentlyItem)
  110. var recItemExsitMap = make(map[string]emby.EmbyRecentlyItem)
  111. var err error
  112. if SkipWatched == false {
  113. em.log.Debugln("Emby Setting SkipWatched = false")
  114. // 默认是不指定某一个User的视频列表
  115. _, err = em.createClient().R().
  116. SetQueryParams(map[string]string{
  117. "api_key": embySettings.APIKey,
  118. "IsUnaired": "false",
  119. "Limit": fmt.Sprintf("%d", maxRequestVideoNumber),
  120. "Recursive": "true",
  121. "SortOrder": "Descending",
  122. "IncludeItemTypes": "Episode,Movie",
  123. "Filters": "IsNotFolder",
  124. "SortBy": "DateCreated",
  125. }).
  126. SetResult(&recItems).
  127. Get(embySettings.AddressUrl + "/emby/Items")
  128. if err != nil {
  129. return emby.EmbyRecentlyItems{}, err
  130. }
  131. } else {
  132. em.log.Debugln("Emby Setting SkipWatched = true")
  133. var userIds emby.EmbyUsers
  134. userIds, err = em.GetUserIdList(embySettings)
  135. if err != nil {
  136. return emby.EmbyRecentlyItems{}, err
  137. }
  138. for _, item := range userIds.Items {
  139. tmpRecItems, err := em.GetRecentItemsByUserID(embySettings, item.Id, maxRequestVideoNumber)
  140. if err != nil {
  141. return emby.EmbyRecentlyItems{}, err
  142. }
  143. // 相同的视频项目,需要判断是否已经看过了,看过的需要排除
  144. // 项目是否相同可以通过 Id 判断
  145. for _, recentlyItem := range tmpRecItems.Items {
  146. // 这个视频是否已经插入过了,可能会进行删除
  147. _, bFound := recItemMap[recentlyItem.Id]
  148. if bFound == false {
  149. // map 中不存在
  150. // 如果没有播放过,则插入
  151. if recentlyItem.UserData.Played == false {
  152. recItemMap[recentlyItem.Id] = recentlyItem
  153. }
  154. } else {
  155. // map 中存在
  156. // 既然存在,则可以理解为其他人是没有看过的,但是,如果当前的用户看过了,那么就要删除这一条
  157. if recentlyItem.UserData.Played == true {
  158. // 先记录下来,然后再删除这一条
  159. recItemExsitMap[recentlyItem.Id] = recentlyItem
  160. }
  161. }
  162. recItemMap[recentlyItem.Id] = recentlyItem
  163. }
  164. }
  165. for id := range recItemExsitMap {
  166. em.log.Debugln("Skip Watched Video:", recItemMap[id].Type, recItemMap[id].Name)
  167. delete(recItemMap, id)
  168. }
  169. for _, item := range recItemMap {
  170. recItems.Items = append(recItems.Items, item)
  171. }
  172. recItems.TotalRecordCount = len(recItemMap)
  173. }
  174. return recItems, nil
  175. }
  176. // GetUserIdList 获取所有的 UserId
  177. func (em *EmbyApi) GetUserIdList(embySettings *settings.EmbySettings) (emby.EmbyUsers, error) {
  178. var recItems emby.EmbyUsers
  179. _, err := em.createClient().R().
  180. SetQueryParams(map[string]string{
  181. "api_key": embySettings.APIKey,
  182. }).
  183. SetResult(&recItems).
  184. Get(embySettings.AddressUrl + "/emby/Users/Query")
  185. if err != nil {
  186. return emby.EmbyUsers{}, err
  187. }
  188. return recItems, nil
  189. }
  190. // GetItemAncestors 获取父级信息,在 API 调试界面 -- LibraryService
  191. func (em *EmbyApi) GetItemAncestors(embySettings *settings.EmbySettings, id string) ([]emby.EmbyItemsAncestors, error) {
  192. var recItems []emby.EmbyItemsAncestors
  193. _, err := em.createClient().R().
  194. SetQueryParams(map[string]string{
  195. "api_key": embySettings.APIKey,
  196. }).
  197. SetResult(&recItems).
  198. Get(embySettings.AddressUrl + "/emby/Items/" + id + "/Ancestors")
  199. if err != nil {
  200. return nil, err
  201. }
  202. return recItems, nil
  203. }
  204. // GetItemVideoInfo 在 API 调试界面 -- UserLibraryService,如果是电影,那么是可以从 ProviderIds 得到 IMDB ID 的
  205. // 如果是连续剧,那么不能使用一集的ID取获取,需要是这个剧集的 ID,注意一季的ID也是不行的
  206. func (em *EmbyApi) GetItemVideoInfo(embySettings *settings.EmbySettings, id string) (emby.EmbyVideoInfo, error) {
  207. var recItem emby.EmbyVideoInfo
  208. _, err := em.createClient().R().
  209. SetQueryParams(map[string]string{
  210. "api_key": embySettings.APIKey,
  211. }).
  212. SetResult(&recItem).
  213. Get(embySettings.AddressUrl + "/emby/LiveTv/Programs/" + id)
  214. if err != nil {
  215. return emby.EmbyVideoInfo{}, err
  216. }
  217. return recItem, nil
  218. }
  219. // GetItemVideoInfoByUserId 可以拿到这个视频的选择字幕Index,配合 GetItemVideoInfo 使用。 在 API 调试界面 -- UserLibraryService
  220. func (em *EmbyApi) GetItemVideoInfoByUserId(embySettings *settings.EmbySettings, userId, videoId string) (emby.EmbyVideoInfoByUserId, error) {
  221. var recItem emby.EmbyVideoInfoByUserId
  222. _, err := em.createClient().R().
  223. SetQueryParams(map[string]string{
  224. "api_key": embySettings.APIKey,
  225. }).
  226. SetResult(&recItem).
  227. Get(embySettings.AddressUrl + "/emby/Users/" + userId + "/Items/" + videoId)
  228. if err != nil {
  229. return emby.EmbyVideoInfoByUserId{}, err
  230. }
  231. return recItem, nil
  232. }
  233. // UpdateVideoSubList 更新字幕列表, 在 API 调试界面 -- ItemRefreshService
  234. func (em *EmbyApi) UpdateVideoSubList(embySettings *settings.EmbySettings, id string) error {
  235. _, err := em.createClient().R().
  236. SetQueryParams(map[string]string{
  237. "Recursive": "true",
  238. "api_key": embySettings.APIKey,
  239. }).
  240. Post(embySettings.AddressUrl + "/emby/Items/" + id + "/Refresh")
  241. if err != nil {
  242. return err
  243. }
  244. return nil
  245. }
  246. // GetSubFileData 下载字幕 subExt -> .ass or .srt , 在 API 调试界面 -- SubtitleService
  247. func (em *EmbyApi) GetSubFileData(embySettings *settings.EmbySettings, videoId, mediaSourceId, subIndex, subExt string) (string, error) {
  248. response, err := em.createClient().R().
  249. Get(embySettings.AddressUrl + "/emby/Videos/" + videoId + "/" + mediaSourceId + "/Subtitles/" + subIndex + "/Stream" + subExt)
  250. if err != nil {
  251. return "", err
  252. }
  253. return response.String(), nil
  254. }
  255. func (em *EmbyApi) createClient() *resty.Client {
  256. // 见 https://github.com/ChineseSubFinder/ChineseSubFinder/issues/140
  257. client := resty.New().SetTransport(&http.Transport{
  258. DisableKeepAlives: true,
  259. MaxIdleConns: 100,
  260. MaxIdleConnsPerHost: 100,
  261. }).RemoveProxy().SetTimeout(em.timeOut)
  262. return client
  263. }
  264. type InputData struct {
  265. Id string
  266. Wg *sync.WaitGroup
  267. }