|
|
|
@ -14,6 +14,7 @@ import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
"github.com/synctv-org/synctv/internal/conf"
|
|
|
|
|
"github.com/synctv-org/synctv/internal/db"
|
|
|
|
|
dbModel "github.com/synctv-org/synctv/internal/model"
|
|
|
|
@ -23,7 +24,9 @@ import (
|
|
|
|
|
"github.com/synctv-org/synctv/proxy"
|
|
|
|
|
"github.com/synctv-org/synctv/server/model"
|
|
|
|
|
"github.com/synctv-org/synctv/utils"
|
|
|
|
|
refreshcache "github.com/synctv-org/synctv/utils/refreshCache"
|
|
|
|
|
"github.com/synctv-org/synctv/vendors/bilibili"
|
|
|
|
|
"github.com/zencoder/go-dash/v3/mpd"
|
|
|
|
|
"github.com/zijiren233/livelib/protocol/hls"
|
|
|
|
|
"github.com/zijiren233/livelib/protocol/httpflv"
|
|
|
|
|
)
|
|
|
|
@ -61,11 +64,17 @@ func MovieList(ctx *gin.Context) {
|
|
|
|
|
|
|
|
|
|
m := room.GetMoviesWithPage(page, max)
|
|
|
|
|
|
|
|
|
|
current, err := genCurrent(room.Current(), user.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mresp := make([]model.MoviesResp, len(m))
|
|
|
|
|
for i, v := range m {
|
|
|
|
|
mresp[i] = model.MoviesResp{
|
|
|
|
|
Id: v.ID,
|
|
|
|
|
Base: m[i].Base,
|
|
|
|
|
Base: v.Base,
|
|
|
|
|
Creator: op.GetUserName(v.CreatorID),
|
|
|
|
|
}
|
|
|
|
|
// hide headers when proxy
|
|
|
|
@ -74,12 +83,6 @@ func MovieList(ctx *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current, err := genCurrent(room.Current(), user.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current.UpdateSeek()
|
|
|
|
|
|
|
|
|
|
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
|
|
|
|
@ -91,7 +94,7 @@ func MovieList(ctx *gin.Context) {
|
|
|
|
|
|
|
|
|
|
func genCurrent(current *op.Current, userID string) (*op.Current, error) {
|
|
|
|
|
if current.Movie.Base.VendorInfo.Vendor != "" {
|
|
|
|
|
return current, parse2VendorMovie(userID, ¤t.Movie, !current.Movie.Base.Proxy)
|
|
|
|
|
return current, parse2VendorMovie(userID, ¤t.Movie)
|
|
|
|
|
}
|
|
|
|
|
return current, nil
|
|
|
|
|
}
|
|
|
|
@ -141,11 +144,12 @@ func Movies(ctx *gin.Context) {
|
|
|
|
|
|
|
|
|
|
m := room.GetMoviesWithPage(int(page), int(max))
|
|
|
|
|
|
|
|
|
|
mresp := make([]model.MoviesResp, len(m))
|
|
|
|
|
mresp := make([]*model.MoviesResp, len(m))
|
|
|
|
|
for i, v := range m {
|
|
|
|
|
mresp[i] = model.MoviesResp{
|
|
|
|
|
logrus.Info(m[i].Base.Headers)
|
|
|
|
|
mresp[i] = &model.MoviesResp{
|
|
|
|
|
Id: v.ID,
|
|
|
|
|
Base: m[i].Base,
|
|
|
|
|
Base: v.Base,
|
|
|
|
|
Creator: op.GetUserName(v.CreatorID),
|
|
|
|
|
}
|
|
|
|
|
// hide headers when proxy
|
|
|
|
@ -480,23 +484,27 @@ func ProxyMovie(ctx *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if m.Base.VendorInfo.Vendor != "" {
|
|
|
|
|
err = parse2VendorMovie(m.Movie.CreatorID, m.Movie, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
proxyVendorMovie(ctx, m.Movie)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l, err := utils.ParseURLIsLocalIP(m.Base.Url); err != nil || l {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("parse url error or url is local ip"))
|
|
|
|
|
err = proxyURL(ctx, m.Base.Url, m.Base.Headers)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hrs := proxy.NewBufferedHttpReadSeeker(256*1024, m.Base.Url,
|
|
|
|
|
func proxyURL(ctx *gin.Context, url string, headers map[string]string) error {
|
|
|
|
|
if l, err := utils.ParseURLIsLocalIP(url); err != nil || l {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
hrs := proxy.NewBufferedHttpReadSeeker(512*1024, url,
|
|
|
|
|
proxy.WithContext(ctx),
|
|
|
|
|
proxy.WithHeaders(m.Base.Headers),
|
|
|
|
|
proxy.WithHeaders(headers),
|
|
|
|
|
)
|
|
|
|
|
http.ServeContent(ctx.Writer, ctx.Request, m.Base.Url, time.Now(), hrs)
|
|
|
|
|
http.ServeContent(ctx.Writer, ctx.Request, url, time.Now(), hrs)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FormatErrNotSupportFileType string
|
|
|
|
@ -578,7 +586,85 @@ func JoinLive(ctx *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parse2VendorMovie(userID string, movie *dbModel.Movie, getUrl bool) (err error) {
|
|
|
|
|
func initBilibiliCache(cookies []*http.Cookie, bvid string, cid, epid uint, roomID, movieID string) *refreshcache.RefreshCache[*dbModel.BilibiliVendorCache] {
|
|
|
|
|
return refreshcache.NewRefreshCache[*dbModel.BilibiliVendorCache](func() (*dbModel.BilibiliVendorCache, error) {
|
|
|
|
|
cli, err := bilibili.NewClient(cookies)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
var m *mpd.MPD
|
|
|
|
|
if bvid != "" && cid != 0 {
|
|
|
|
|
m, err = cli.GetDashVideoURL(0, bvid, cid)
|
|
|
|
|
} else if epid != 0 {
|
|
|
|
|
m, err = cli.GetDashPGCURL(epid, 0)
|
|
|
|
|
} else {
|
|
|
|
|
return nil, errors.New("bvid and epid are empty")
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
m.BaseURL = append(m.BaseURL, fmt.Sprintf("/api/movie/proxy/%s/", roomID))
|
|
|
|
|
id := 0
|
|
|
|
|
movies := []string{}
|
|
|
|
|
for _, as := range m.GetCurrentPeriod().AdaptationSets {
|
|
|
|
|
for _, r := range as.Representations {
|
|
|
|
|
for i := range r.BaseURL {
|
|
|
|
|
movies = append(movies, r.BaseURL[i])
|
|
|
|
|
r.BaseURL[i] = fmt.Sprintf("%s?id=%d", movieID, id)
|
|
|
|
|
id++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s, err := m.WriteToString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &dbModel.BilibiliVendorCache{
|
|
|
|
|
URLs: movies,
|
|
|
|
|
MPDFile: s,
|
|
|
|
|
}, nil
|
|
|
|
|
}, time.Minute*119)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func proxyVendorMovie(ctx *gin.Context, movie *dbModel.Movie) {
|
|
|
|
|
switch movie.Base.VendorInfo.Vendor {
|
|
|
|
|
case dbModel.StreamingVendorBilibili:
|
|
|
|
|
info := movie.Base.VendorInfo.Bilibili
|
|
|
|
|
bvc, err := movie.Base.VendorInfo.Bilibili.InitOrLoadCache(func() *refreshcache.RefreshCache[*dbModel.BilibiliVendorCache] {
|
|
|
|
|
vendor, err := db.FirstOrInitVendorByUserIDAndVendor(movie.CreatorID, dbModel.StreamingVendorBilibili)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return initBilibiliCache(vendor.Cookies, info.Bvid, info.Cid, info.Epid, movie.RoomID, movie.ID)
|
|
|
|
|
}).Get()
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if id := ctx.Query("id"); id == "" {
|
|
|
|
|
ctx.Data(http.StatusOK, "application/dash+xml", []byte(bvc.MPDFile))
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
streamId, err := strconv.Atoi(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if streamId >= len(bvc.URLs) {
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("stream id out of range"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
proxyURL(ctx, bvc.URLs[streamId], movie.Base.Headers)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("vendor not support"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parse2VendorMovie(userID string, movie *dbModel.Movie) (err error) {
|
|
|
|
|
if movie.Base.VendorInfo.Shared {
|
|
|
|
|
userID = movie.CreatorID
|
|
|
|
|
}
|
|
|
|
@ -596,7 +682,7 @@ func parse2VendorMovie(userID string, movie *dbModel.Movie, getUrl bool) (err er
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if getUrl {
|
|
|
|
|
if !movie.Base.Proxy {
|
|
|
|
|
var mu *bilibili.VideoURL
|
|
|
|
|
if info.Bvid != "" {
|
|
|
|
|
mu, err = cli.GetVideoURL(0, info.Bvid, info.Cid, bilibili.WithQuality(info.Quality))
|
|
|
|
@ -609,6 +695,8 @@ func parse2VendorMovie(userID string, movie *dbModel.Movie, getUrl bool) (err er
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
movie.Base.Url = mu.URL
|
|
|
|
|
} else {
|
|
|
|
|
movie.Base.Type = "mpd"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|