emby_api.go 8.3 KB

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