123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package emby_api
- import (
- "fmt"
- "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
- "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
- "github.com/allanpk716/ChineseSubFinder/internal/types/emby"
- "github.com/go-resty/resty/v2"
- "github.com/panjf2000/ants/v2"
- "golang.org/x/net/context"
- "net/http"
- "sync"
- "time"
- )
- type EmbyApi struct {
- embyConfig settings.EmbySettings
- threads int
- timeOut time.Duration
- client *resty.Client
- }
- func NewEmbyApi(embyConfig settings.EmbySettings) *EmbyApi {
- em := EmbyApi{}
- em.embyConfig = embyConfig
- // 检查是否超过范围
- em.embyConfig.Check()
- // 强制设置
- em.threads = 6
- em.timeOut = 5 * 60 * time.Second
- // 见 https://github.com/allanpk716/ChineseSubFinder/issues/140
- em.client = resty.New().SetTransport(&http.Transport{
- MaxIdleConns: 100,
- MaxIdleConnsPerHost: 100,
- }).RemoveProxy().SetTimeout(em.timeOut)
- return &em
- }
- // RefreshRecentlyVideoInfo 字幕下载完毕一次,就可以触发一次这个。并发 6 线程去刷新
- func (em EmbyApi) RefreshRecentlyVideoInfo() error {
- items, err := em.GetRecentlyItems()
- if err != nil {
- return err
- }
- log_helper.GetLogger().Debugln("RefreshRecentlyVideoInfo - GetRecentlyItems Count", len(items.Items))
- updateFunc := func(i interface{}) error {
- tmpId := i.(string)
- return em.UpdateVideoSubList(tmpId)
- }
- p, err := ants.NewPoolWithFunc(em.threads, func(inData interface{}) {
- data := inData.(InputData)
- defer data.Wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), em.timeOut)
- defer cancel()
- done := make(chan error, 1)
- panicChan := make(chan interface{}, 1)
- go func() {
- defer func() {
- if p := recover(); p != nil {
- panicChan <- p
- }
- }()
- done <- updateFunc(data.Id)
- }()
- select {
- case err = <-done:
- if err != nil {
- log_helper.GetLogger().Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got error", err)
- }
- return
- case p := <-panicChan:
- log_helper.GetLogger().Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got panic", p)
- case <-ctx.Done():
- log_helper.GetLogger().Errorln("RefreshRecentlyVideoInfo.NewPoolWithFunc got time out", ctx.Err())
- return
- }
- })
- if err != nil {
- return err
- }
- defer p.Release()
- wg := sync.WaitGroup{}
- for _, item := range items.Items {
- wg.Add(1)
- err = p.Invoke(InputData{Id: item.Id, Wg: &wg})
- if err != nil {
- log_helper.GetLogger().Errorln("RefreshRecentlyVideoInfo ants.Invoke", err)
- }
- }
- wg.Wait()
- return nil
- }
- // GetRecentlyItems 获取近期的视频,在 API 调试界面 -- ItemsService
- func (em EmbyApi) GetRecentlyItems() (emby.EmbyRecentlyItems, error) {
- var recItems emby.EmbyRecentlyItems
- recItems.Items = make([]emby.EmbyRecentlyItem, 0)
- var recItemMap = make(map[string]emby.EmbyRecentlyItem)
- var recItemExsitMap = make(map[string]emby.EmbyRecentlyItem)
- var err error
- if em.embyConfig.SkipWatched == false {
- log_helper.GetLogger().Debugln("Emby Setting SkipWatched = false")
- // 默认是不指定某一个User的视频列表
- _, err = em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- "IsUnaired": "false",
- "Limit": fmt.Sprintf("%d", em.embyConfig.MaxRequestVideoNumber),
- "Recursive": "true",
- "SortOrder": "Descending",
- "IncludeItemTypes": "Episode,Movie",
- "Filters": "IsNotFolder",
- "SortBy": "DateCreated",
- }).
- SetResult(&recItems).
- Get(em.embyConfig.AddressUrl + "/emby/Items")
- if err != nil {
- return emby.EmbyRecentlyItems{}, err
- }
- } else {
- log_helper.GetLogger().Debugln("Emby Setting SkipWatched = true")
- var userIds emby.EmbyUsers
- userIds, err = em.GetUserIdList()
- if err != nil {
- return emby.EmbyRecentlyItems{}, err
- }
- for _, item := range userIds.Items {
- var tmpRecItems emby.EmbyRecentlyItems
- // 获取指定用户的视频列表
- _, err = em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- "IsUnaired": "false",
- "Limit": fmt.Sprintf("%d", em.embyConfig.MaxRequestVideoNumber),
- "Recursive": "true",
- "SortOrder": "Descending",
- "IncludeItemTypes": "Episode,Movie",
- "Filters": "IsNotFolder",
- "SortBy": "DateCreated",
- }).
- SetResult(&tmpRecItems).
- Get(em.embyConfig.AddressUrl + "/emby/Users/" + item.Id + "/Items")
- if err != nil {
- return emby.EmbyRecentlyItems{}, err
- }
- // 相同的视频项目,需要判断是否已经看过了,看过的需要排除
- // 项目是否相同可以通过 Id 判断
- for _, recentlyItem := range tmpRecItems.Items {
- // 这个视频是否已经插入过了,可能会进行删除
- _, bFound := recItemMap[recentlyItem.Id]
- if bFound == false {
- // map 中不存在
- // 如果没有播放过,则插入
- if recentlyItem.UserData.Played == false {
- recItemMap[recentlyItem.Id] = recentlyItem
- }
- } else {
- // map 中存在
- // 既然存在,则可以理解为其他人是没有看过的,但是,如果当前的用户看过了,那么就要删除这一条
- if recentlyItem.UserData.Played == true {
- // 先记录下来,然后再删除这一条
- recItemExsitMap[recentlyItem.Id] = recentlyItem
- }
- }
- recItemMap[recentlyItem.Id] = recentlyItem
- }
- }
- for id := range recItemExsitMap {
- log_helper.GetLogger().Debugln("Skip Watched Video:", recItemMap[id].Type, recItemMap[id].Name)
- delete(recItemMap, id)
- }
- for _, item := range recItemMap {
- recItems.Items = append(recItems.Items, item)
- }
- recItems.TotalRecordCount = len(recItemMap)
- }
- return recItems, nil
- }
- // GetUserIdList 获取所有的 UserId
- func (em EmbyApi) GetUserIdList() (emby.EmbyUsers, error) {
- var recItems emby.EmbyUsers
- _, err := em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- }).
- SetResult(&recItems).
- Get(em.embyConfig.AddressUrl + "/emby/Users/Query")
- if err != nil {
- return emby.EmbyUsers{}, err
- }
- return recItems, nil
- }
- // GetItemAncestors 获取父级信息,在 API 调试界面 -- LibraryService
- func (em EmbyApi) GetItemAncestors(id string) ([]emby.EmbyItemsAncestors, error) {
- var recItems []emby.EmbyItemsAncestors
- _, err := em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- }).
- SetResult(&recItems).
- Get(em.embyConfig.AddressUrl + "/emby/Items/" + id + "/Ancestors")
- if err != nil {
- return nil, err
- }
- return recItems, nil
- }
- // GetItemVideoInfo 在 API 调试界面 -- UserLibraryService
- func (em EmbyApi) GetItemVideoInfo(id string) (emby.EmbyVideoInfo, error) {
- var recItem emby.EmbyVideoInfo
- _, err := em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- }).
- SetResult(&recItem).
- Get(em.embyConfig.AddressUrl + "/emby/LiveTv/Programs/" + id)
- if err != nil {
- return emby.EmbyVideoInfo{}, err
- }
- return recItem, nil
- }
- // GetItemVideoInfoByUserId 可以拿到这个视频的选择字幕Index,配合 GetItemVideoInfo 使用。 在 API 调试界面 -- UserLibraryService
- func (em EmbyApi) GetItemVideoInfoByUserId(userId, videoId string) (emby.EmbyVideoInfoByUserId, error) {
- var recItem emby.EmbyVideoInfoByUserId
- _, err := em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- }).
- SetResult(&recItem).
- Get(em.embyConfig.AddressUrl + "/emby/Users/" + userId + "/Items/" + videoId)
- if err != nil {
- return emby.EmbyVideoInfoByUserId{}, err
- }
- return recItem, nil
- }
- // UpdateVideoSubList 更新字幕列表, 在 API 调试界面 -- ItemRefreshService
- func (em EmbyApi) UpdateVideoSubList(id string) error {
- _, err := em.client.R().
- SetQueryParams(map[string]string{
- "api_key": em.embyConfig.APIKey,
- }).
- Post(em.embyConfig.AddressUrl + "/emby/Items/" + id + "/Refresh")
- if err != nil {
- return err
- }
- return nil
- }
- // GetSubFileData 下载字幕 subExt -> .ass or .srt , 在 API 调试界面 -- SubtitleService
- func (em EmbyApi) GetSubFileData(videoId, mediaSourceId, subIndex, subExt string) (string, error) {
- response, err := em.client.R().
- Get(em.embyConfig.AddressUrl + "/emby/Videos/" + videoId + "/" + mediaSourceId + "/Subtitles/" + subIndex + "/Stream" + subExt)
- if err != nil {
- return "", err
- }
- return response.String(), nil
- }
- type InputData struct {
- Id string
- Wg *sync.WaitGroup
- }
|