Feat: bilibili cache

pull/31/head
zijiren233 2 years ago
parent c0175bacad
commit 8c906a28a2

@ -10,6 +10,7 @@ import (
"github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/utils" "github.com/synctv-org/synctv/utils"
refreshcache "github.com/synctv-org/synctv/utils/refreshCache" refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
"github.com/zijiren233/gencontainer/rwmap"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -151,26 +152,48 @@ type VendorInfo struct {
} }
type BilibiliVendorInfo struct { type BilibiliVendorInfo struct {
Bvid string `json:"bvid,omitempty"` Bvid string `json:"bvid,omitempty"`
Cid uint `json:"cid,omitempty"` Cid uint `json:"cid,omitempty"`
Epid uint `json:"epid,omitempty"` Epid uint `json:"epid,omitempty"`
Quality uint `json:"quality,omitempty"` Quality uint `json:"quality,omitempty"`
Cache atomic.Pointer[refreshcache.RefreshCache[*BilibiliVendorCache]] `gorm:"-:all" json:"-"` Cache BilibiliVendorCache `gorm:"-:all" json:"-"`
} }
type BilibiliVendorCache struct { type BilibiliVendorCache struct {
URL rwmap.RWMap[string, *refreshcache.RefreshCache[string]]
MPD atomic.Pointer[refreshcache.RefreshCache[*MPDCache]]
}
type MPDCache struct {
MPDFile string MPDFile string
URLs []string URLs []string
} }
func (b *BilibiliVendorInfo) InitOrLoadCache(initCache func() *refreshcache.RefreshCache[*BilibiliVendorCache]) *refreshcache.RefreshCache[*BilibiliVendorCache] { func (b *BilibiliVendorInfo) InitOrLoadURLCache(id string, initCache func(*BilibiliVendorInfo) (*refreshcache.RefreshCache[string], error)) (*refreshcache.RefreshCache[string], error) {
if c := b.Cache.Load(); c != nil { if c, loaded := b.Cache.URL.Load(id); loaded {
return c return c, nil
}
c, err := initCache(b)
if err != nil {
return nil, err
}
c, _ = b.Cache.URL.LoadOrStore(id, c)
return c, nil
}
func (b *BilibiliVendorInfo) InitOrLoadMPDCache(initCache func(*BilibiliVendorInfo) (*refreshcache.RefreshCache[*MPDCache], error)) (*refreshcache.RefreshCache[*MPDCache], error) {
if c := b.Cache.MPD.Load(); c != nil {
return c, nil
}
c, err := initCache(b)
if err != nil {
return nil, err
} }
c := initCache() if b.Cache.MPD.CompareAndSwap(nil, c) {
if b.Cache.CompareAndSwap(nil, c) { return c, nil
return c
} else { } else {
return b.Cache.Load() return b.InitOrLoadMPDCache(initCache)
} }
} }

@ -612,8 +612,8 @@ func JoinLive(ctx *gin.Context) {
} }
} }
func initBilibiliCache(cookies []*http.Cookie, bvid string, cid, epid uint, roomID, movieID string) *refreshcache.RefreshCache[*dbModel.BilibiliVendorCache] { func initBilibiliMPDCache(cookies []*http.Cookie, bvid string, cid, epid uint, roomID, movieID string) *refreshcache.RefreshCache[*dbModel.MPDCache] {
return refreshcache.NewRefreshCache[*dbModel.BilibiliVendorCache](func() (*dbModel.BilibiliVendorCache, error) { return refreshcache.NewRefreshCache[*dbModel.MPDCache](func() (*dbModel.MPDCache, error) {
cli, err := bilibili.NewClient(cookies) cli, err := bilibili.NewClient(cookies)
if err != nil { if err != nil {
return nil, err return nil, err
@ -645,30 +645,56 @@ func initBilibiliCache(cookies []*http.Cookie, bvid string, cid, epid uint, room
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &dbModel.BilibiliVendorCache{ return &dbModel.MPDCache{
URLs: movies, URLs: movies,
MPDFile: s, MPDFile: s,
}, nil }, nil
}, time.Minute*119) }, time.Minute*119)
} }
func initBilibiliShareCache(cookies []*http.Cookie, bvid string, cid, epid uint) *refreshcache.RefreshCache[string] {
return refreshcache.NewRefreshCache[string](func() (string, error) {
cli, err := bilibili.NewClient(cookies)
if err != nil {
return "", err
}
var mu *bilibili.VideoURL
if bvid != "" {
mu, err = cli.GetVideoURL(0, bvid, cid)
} else if epid != 0 {
mu, err = cli.GetPGCURL(epid, 0)
} else {
err = errors.New("bvid and epid are empty")
}
if err != nil {
return "", err
}
return mu.URL, nil
}, time.Minute*119)
}
func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) { func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) {
switch movie.Base.VendorInfo.Vendor { switch movie.Base.VendorInfo.Vendor {
case dbModel.StreamingVendorBilibili: case dbModel.StreamingVendorBilibili:
info := movie.Base.VendorInfo.Bilibili bvc, err := movie.Base.VendorInfo.Bilibili.InitOrLoadMPDCache(func(info *dbModel.BilibiliVendorInfo) (*refreshcache.RefreshCache[*dbModel.MPDCache], error) {
bvc, err := movie.Base.VendorInfo.Bilibili.InitOrLoadCache(func() *refreshcache.RefreshCache[*dbModel.BilibiliVendorCache] {
vendor, err := db.FirstOrInitVendorByUserIDAndVendor(movie.CreatorID, dbModel.StreamingVendorBilibili) vendor, err := db.FirstOrInitVendorByUserIDAndVendor(movie.CreatorID, dbModel.StreamingVendorBilibili)
if err != nil { if err != nil {
return nil return nil, err
} }
return initBilibiliCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid, movie.RoomID, movie.ID) return initBilibiliMPDCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid, movie.RoomID, movie.ID), nil
}).Get() })
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
mpd, err := bvc.Get()
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return return
} }
if id := ctx.Query("id"); id == "" { if id := ctx.Query("id"); id == "" {
ctx.Data(http.StatusOK, "application/dash+xml", []byte(bvc.MPDFile)) ctx.Data(http.StatusOK, "application/dash+xml", []byte(mpd.MPDFile))
return return
} else { } else {
streamId, err := strconv.Atoi(id) streamId, err := strconv.Atoi(id)
@ -676,11 +702,11 @@ func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
} }
if streamId >= len(bvc.URLs) { if streamId >= len(mpd.URLs) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("stream id out of range")) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("stream id out of range"))
return return
} }
proxyURL(ctx, bvc.URLs[streamId], movie.Base.Headers) proxyURL(ctx, mpd.URLs[streamId], movie.Base.Headers)
return return
} }
@ -699,28 +725,24 @@ func parse2VendorMovie(userID string, movie *dbModel.Movie) (err error) {
case dbModel.StreamingVendorBilibili: case dbModel.StreamingVendorBilibili:
info := movie.Base.VendorInfo.Bilibili info := movie.Base.VendorInfo.Bilibili
vendor, err := db.AssignFirstOrCreateVendorByUserIDAndVendor(userID, dbModel.StreamingVendorBilibili)
if err != nil {
return err
}
cli, err := bilibili.NewClient(vendor.Cookies)
if err != nil {
return err
}
if !movie.Base.Proxy { if !movie.Base.Proxy {
var mu *bilibili.VideoURL c, err := movie.Base.VendorInfo.Bilibili.InitOrLoadURLCache(userID, func(bvi *dbModel.BilibiliVendorInfo) (*refreshcache.RefreshCache[string], error) {
if info.Bvid != "" { vendor, err := db.FirstOrInitVendorByUserIDAndVendor(userID, dbModel.StreamingVendorBilibili)
mu, err = cli.GetVideoURL(0, info.Bvid, info.Cid, bilibili.WithQuality(info.Quality)) if err != nil {
} else if info.Epid != 0 { return nil, err
mu, err = cli.GetPGCURL(info.Epid, 0, bilibili.WithQuality(info.Quality)) }
} else { return initBilibiliShareCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid), nil
err = errors.New("bvid and epid are empty") })
if err != nil {
return err
} }
data, err := c.Get()
if err != nil { if err != nil {
return err return err
} }
movie.Base.Url = mu.URL
movie.Base.Url = data
} else { } else {
movie.Base.Type = "mpd" movie.Base.Type = "mpd"
} }

Loading…
Cancel
Save