diff --git a/utils/refreshCache/refreshCache.go b/utils/refreshCache/refreshCache.go new file mode 100644 index 0000000..77978f7 --- /dev/null +++ b/utils/refreshCache/refreshCache.go @@ -0,0 +1,48 @@ +package refreshcache + +import ( + "sync" + "time" +) + +type RefreshCache[T any] struct { + lock sync.RWMutex + last time.Time + maxAge time.Duration + refreshFunc func() (T, error) + data T +} + +func NewRefreshCache[T any](refreshFunc func() (T, error), maxAge time.Duration) *RefreshCache[T] { + if refreshFunc == nil { + panic("refreshFunc cannot be nil") + } + if maxAge <= 0 { + panic("maxAge must be positive") + } + return &RefreshCache[T]{ + refreshFunc: refreshFunc, + maxAge: maxAge, + } +} + +func (r *RefreshCache[T]) Get() (data T, err error) { + r.lock.RLock() + if time.Since(r.last) < r.maxAge { + r.lock.RUnlock() + return r.data, nil + } + r.lock.RUnlock() + r.lock.Lock() + defer r.lock.Unlock() + if time.Since(r.last) < r.maxAge { + return r.data, nil + } + defer func() { + if err == nil { + r.data = data + r.last = time.Now() + } + }() + return r.refreshFunc() +} diff --git a/vendors/bilibili/buvid.go b/vendors/bilibili/buvid.go index 5ccdf52..a9cf5df 100644 --- a/vendors/bilibili/buvid.go +++ b/vendors/bilibili/buvid.go @@ -3,58 +3,46 @@ package bilibili import ( "errors" "net/http" - "sync" "time" json "github.com/json-iterator/go" "github.com/synctv-org/synctv/utils" + refreshcache "github.com/synctv-org/synctv/utils/refreshCache" ) -var ( - bLock sync.RWMutex - b3, b4 string - bLastUpdateTime time.Time -) +type buvid struct { + b3, b4 string +} + +var buvidCache = refreshcache.NewRefreshCache[buvid](func() (buvid, error) { + b3, b4, err := newBuvid() + if err != nil { + return buvid{}, err + } + return buvid{ + b3: b3, + b4: b4, + }, nil +}, time.Hour) func getBuvidCookies() ([]*http.Cookie, error) { - b3, b4, err := getBuvid() + buvid, err := buvidCache.Get() if err != nil { return nil, err } return []*http.Cookie{ { Name: "buvid3", - Value: b3, + Value: buvid.b3, }, { Name: "buvid4", - Value: b4, + Value: buvid.b4, }, }, nil } -func getBuvid() (string, string, error) { - bLock.RLock() - if time.Since(bLastUpdateTime) < time.Hour { - bLock.RUnlock() - return b3, b4, nil - } - bLock.RUnlock() - bLock.Lock() - defer bLock.Unlock() - if time.Since(bLastUpdateTime) < time.Hour { - return b3, b4, nil - } - var err error - b3, b4, err = newBuvid() - if err != nil { - return "", "", err - } - bLastUpdateTime = time.Now() - return b3, b4, nil -} - -type buvid struct { +type spiResp struct { Code int `json:"code"` Data struct { B3 string `json:"b_3"` @@ -75,7 +63,7 @@ func newBuvid() (string, string, error) { return "", "", err } defer resp.Body.Close() - var b buvid + var b spiResp err = json.NewDecoder(resp.Body).Decode(&b) if err != nil { return "", "", err diff --git a/vendors/bilibili/wbi.go b/vendors/bilibili/wbi.go index 3b2bc84..776f84c 100644 --- a/vendors/bilibili/wbi.go +++ b/vendors/bilibili/wbi.go @@ -8,11 +8,11 @@ import ( "sort" "strconv" "strings" - "sync" "time" json "github.com/json-iterator/go" "github.com/synctv-org/synctv/utils" + refreshcache "github.com/synctv-org/synctv/utils/refreshCache" ) var ( @@ -22,17 +22,25 @@ var ( 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52, } - lock sync.RWMutex - imgKey, subKey string - lastUpdateTime time.Time + wbiCache = refreshcache.NewRefreshCache[key](func() (key, error) { + imgKey, subKey, err := getWbiKeys() + if err != nil { + return key{}, err + } + return key{imgKey, subKey}, nil + }, time.Minute*10) ) +type key struct { + imgKey, subKey string +} + func signAndGenerateURL(urlStr string) (string, error) { urlObj, err := url.Parse(urlStr) if err != nil { return "", err } - imgKey, subKey, err := getWbiKeysCached() + key, err := wbiCache.Get() if err != nil { return "", err } @@ -41,7 +49,7 @@ func signAndGenerateURL(urlStr string) (string, error) { for k, v := range query { params[k] = v[0] } - newParams := encWbi(params, imgKey, subKey) + newParams := encWbi(params, key.imgKey, key.subKey) for k, v := range newParams { query.Set(k, v) } @@ -94,27 +102,6 @@ func sanitizeString(s string) string { return s } -func getWbiKeysCached() (string, string, error) { - lock.RLock() - if time.Since(lastUpdateTime).Minutes() < 10 { - defer lock.RUnlock() - return imgKey, subKey, nil - } - lock.RUnlock() - lock.Lock() - defer lock.Unlock() - if time.Since(lastUpdateTime).Minutes() < 10 { - return imgKey, subKey, nil - } - var err error - imgKey, subKey, err = getWbiKeys() - if err != nil { - return "", "", err - } - lastUpdateTime = time.Now() - return imgKey, subKey, nil -} - func getWbiKeys() (string, string, error) { req, err := http.NewRequest(http.MethodGet, "https://api.bilibili.com/x/web-interface/nav", nil) if err != nil {