diff --git a/internal/cache/alist.go b/internal/cache/alist.go index 38cb245..cc7602e 100644 --- a/internal/cache/alist.go +++ b/internal/cache/alist.go @@ -82,7 +82,7 @@ func AlistAuthorizationCacheWithConfigInitFunc(ctx context.Context, v *model.Ali type AlistMovieCache = refreshcache1.RefreshCache[*AlistMovieCacheData, *AlistMovieCacheFuncArgs] func NewAlistMovieCache(movie *model.Movie, subPath string) *AlistMovieCache { - return refreshcache1.NewRefreshCache(NewAlistMovieCacheInitFunc(movie, subPath), time.Minute*14) + return refreshcache1.NewRefreshCache(NewAlistMovieCacheInitFunc(movie, subPath), -1) } type AlistProvider = string @@ -100,18 +100,22 @@ type AlistSubtitle struct { } type AlistMovieCacheData struct { - Ali *AlistAliCache + Ali *refreshcache0.RefreshCache[*AlistAliCache] URL string Provider string Subtitles []*AlistSubtitle } type AlistAliCache struct { + URL string M3U8ListFile []byte + Subtitles []*AlistSubtitle } type SubtitleDataCache = refreshcache0.RefreshCache[[]byte] +const subtitleMaxLength = 15 * 1024 * 1024 + func newAliSubtitles(list []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscodingSubtitleTaskList) []*AlistSubtitle { caches := make([]*AlistSubtitle, len(list)) for i, v := range list { @@ -133,7 +137,10 @@ func newAliSubtitles(list []*alist.FsOtherResp_VideoPreviewPlayInfo_LiveTranscod if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("status code: %d", resp.StatusCode) } - return io.ReadAll(resp.Body) + if resp.ContentLength > subtitleMaxLength { + return nil, fmt.Errorf("subtitle too large, got: %d, max: %d", resp.ContentLength, subtitleMaxLength) + } + return io.ReadAll(io.LimitReader(resp.Body, subtitleMaxLength)) }, -1), Name: v.Language, URL: v.Url, @@ -201,9 +208,7 @@ func NewAlistMovieCacheInitFunc(movie *model.Movie, subPath string) func(ctx con } if fg.Provider == AlistProviderAli { - if err := processAliProvider(ctx, cli, aucd, truePath, movie.MovieBase.VendorInfo.Alist.Password, cache); err != nil { - return nil, err - } + processAliProvider(ctx, fg.RawUrl, cli, aucd, truePath, movie.MovieBase.VendorInfo.Alist.Password, cache) } return cache, nil @@ -291,24 +296,44 @@ func fetchSubtitleContent(ctx context.Context, url string) ([]byte, error) { if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("status code: %d", resp.StatusCode) } - return io.ReadAll(resp.Body) -} - -func processAliProvider(ctx context.Context, cli alist.AlistHTTPServer, aucd *AlistUserCacheData, truePath, password string, cache *AlistMovieCacheData) error { - fo, err := cli.FsOther(ctx, &alist.FsOtherReq{ - Host: aucd.Host, - Token: aucd.Token, - Path: truePath, - Password: password, - Method: "video_preview", - }) - if err != nil { - return err + if resp.ContentLength > subtitleMaxLength { + return nil, fmt.Errorf("subtitle too large, got: %d, max: %d", resp.ContentLength, subtitleMaxLength) } + return io.ReadAll(io.LimitReader(resp.Body, subtitleMaxLength)) +} - cache.Ali = &AlistAliCache{ - M3U8ListFile: genAliM3U8ListFile(fo.VideoPreviewPlayInfo.LiveTranscodingTaskList), - } - cache.Subtitles = append(cache.Subtitles, newAliSubtitles(fo.VideoPreviewPlayInfo.LiveTranscodingSubtitleTaskList)...) - return nil +func processAliProvider(_ context.Context, firstURL string, cli alist.AlistHTTPServer, aucd *AlistUserCacheData, truePath, password string, cache *AlistMovieCacheData) { + cache.Ali = refreshcache0.NewRefreshCache(func(ctx context.Context) (*AlistAliCache, error) { + var url string + if firstURL != "" { + url = firstURL + firstURL = "" + } else { + u, err := cli.FsGet(ctx, &alist.FsGetReq{ + Host: aucd.Host, + Token: aucd.Token, + Path: truePath, + Password: password, + }) + if err != nil { + return nil, err + } + url = u.RawUrl + } + fo, err := cli.FsOther(ctx, &alist.FsOtherReq{ + Host: aucd.Host, + Token: aucd.Token, + Path: truePath, + Password: password, + Method: "video_preview", + }) + if err != nil { + return nil, err + } + return &AlistAliCache{ + URL: url, + M3U8ListFile: genAliM3U8ListFile(fo.VideoPreviewPlayInfo.LiveTranscodingTaskList), + Subtitles: newAliSubtitles(fo.VideoPreviewPlayInfo.LiveTranscodingSubtitleTaskList), + }, nil + }, 14*time.Minute) } diff --git a/internal/op/movie.go b/internal/op/movie.go index e7f76f2..ec34a5b 100644 --- a/internal/op/movie.go +++ b/internal/op/movie.go @@ -46,7 +46,7 @@ func (m *Movie) ExpireID() uint64 { case m.Movie.MovieBase.VendorInfo.Vendor == model.VendorAlist: amcd, _ := m.AlistCache().Raw() if amcd != nil && amcd.Ali != nil { - return uint64(m.AlistCache().Last()) + return uint64(amcd.Ali.Last()) } case m.Movie.MovieBase.Live && m.Movie.MovieBase.VendorInfo.Vendor == model.VendorBilibili: return uint64(m.BilibiliCache().Live.Last()) @@ -62,7 +62,7 @@ func (m *Movie) CheckExpired(expireID uint64) bool { case m.Movie.MovieBase.VendorInfo.Vendor == model.VendorAlist: amcd, _ := m.AlistCache().Raw() if amcd != nil && amcd.Ali != nil { - return time.Now().UnixNano()-int64(expireID) > m.AlistCache().MaxAge() + return time.Now().UnixNano()-int64(amcd.Ali.Last()) > amcd.Ali.MaxAge() } case m.Movie.MovieBase.Live && m.Movie.MovieBase.VendorInfo.Vendor == model.VendorBilibili: return time.Now().UnixNano()-int64(expireID) > m.BilibiliCache().Live.MaxAge() diff --git a/server/handlers/movie.go b/server/handlers/movie.go index 03c35c7..241ca2a 100644 --- a/server/handlers/movie.go +++ b/server/handlers/movie.go @@ -634,13 +634,6 @@ func ServeM3u8(ctx *gin.Context) { return } - if m.Movie.MovieBase.VendorInfo.Vendor != "" { - err := errors.New("vendor is not supported") - log.Errorf("get vendor service error: %v", err) - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(err)) - return - } - if m.Movie.MovieBase.RtmpSource { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("this movie is rtmp source, not support use this method proxy")) return diff --git a/server/handlers/proxy/m3u8.go b/server/handlers/proxy/m3u8.go index a70662c..cf84b62 100644 --- a/server/handlers/proxy/m3u8.go +++ b/server/handlers/proxy/m3u8.go @@ -56,6 +56,42 @@ func NewM3u8TargetToken(targetURL, roomID, movieID string, isM3u8File bool) (str const maxM3u8FileSize = 3 * 1024 * 1024 // +func M3u8Data(ctx *gin.Context, data []byte, baseURL string, token, roomID, movieID string) error { + hasM3u8File := false + err := m3u8.RangeM3u8SegmentsWithBaseURL(stream.BytesToString(data), baseURL, func(segmentUrl string) (bool, error) { + if utils.IsM3u8Url(segmentUrl) { + hasM3u8File = true + return false, nil + } + return true, nil + }) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, + model.NewAPIErrorStringResp( + fmt.Sprintf("range m3u8 segments with base url error: %v", err), + ), + ) + return fmt.Errorf("range m3u8 segments with base url error: %w", err) + } + m3u8Str, err := m3u8.ReplaceM3u8SegmentsWithBaseURL(stream.BytesToString(data), baseURL, func(segmentUrl string) (string, error) { + targetToken, err := NewM3u8TargetToken(segmentUrl, roomID, movieID, hasM3u8File) + if err != nil { + return "", err + } + return fmt.Sprintf("/api/room/movie/proxy/%s/m3u8/%s?token=%s&roomId=%s", movieID, targetToken, token, roomID), nil + }) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, + model.NewAPIErrorStringResp( + fmt.Sprintf("replace m3u8 segments with base url error: %v", err), + ), + ) + return fmt.Errorf("replace m3u8 segments with base url error: %w", err) + } + ctx.Data(http.StatusOK, hls.M3U8ContentType, stream.StringToBytes(m3u8Str)) + return nil +} + // only cache non-m3u8 files func M3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File bool, token, roomID, movieID string, opts ...Option) error { if !isM3u8File { @@ -110,37 +146,5 @@ func M3u8(ctx *gin.Context, u string, headers map[string]string, isM3u8File bool ) return fmt.Errorf("read response body error: %w", err) } - hasM3u8File := false - err = m3u8.RangeM3u8SegmentsWithBaseURL(stream.BytesToString(b), u, func(segmentUrl string) (bool, error) { - if utils.IsM3u8Url(segmentUrl) { - hasM3u8File = true - return false, nil - } - return true, nil - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, - model.NewAPIErrorStringResp( - fmt.Sprintf("range m3u8 segments with base url error: %v", err), - ), - ) - return fmt.Errorf("range m3u8 segments with base url error: %w", err) - } - m3u8Str, err := m3u8.ReplaceM3u8SegmentsWithBaseURL(stream.BytesToString(b), u, func(segmentUrl string) (string, error) { - targetToken, err := NewM3u8TargetToken(segmentUrl, roomID, movieID, hasM3u8File) - if err != nil { - return "", err - } - return fmt.Sprintf("/api/room/movie/proxy/%s/m3u8/%s?token=%s&roomId=%s", movieID, targetToken, token, roomID), nil - }) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, - model.NewAPIErrorStringResp( - fmt.Sprintf("replace m3u8 segments with base url error: %v", err), - ), - ) - return fmt.Errorf("replace m3u8 segments with base url error: %w", err) - } - ctx.Data(http.StatusOK, hls.M3U8ContentType, stream.StringToBytes(m3u8Str)) - return nil + return M3u8Data(ctx, b, u, token, roomID, movieID) } diff --git a/server/handlers/proxy/slice.go b/server/handlers/proxy/slice.go index a88bd10..4384e73 100644 --- a/server/handlers/proxy/slice.go +++ b/server/handlers/proxy/slice.go @@ -266,7 +266,7 @@ func (c *SliceCacheProxy) fetchFromSource(offset int64) (*CacheItem, error) { return nil, fmt.Errorf("failed to get content total length from source: %w", err) } if total != offset+int64(n) { - return nil, fmt.Errorf("source content total length mismatch, got: %d, expected: %d, %w", total, offset+int64(n), err) + return nil, fmt.Errorf("source content total length mismatch, got: %d, expected: %d, %w", total, offset+int64(n), io.ErrUnexpectedEOF) } } diff --git a/server/handlers/vendors/vendorAlist/alist.go b/server/handlers/vendors/vendorAlist/alist.go index 3d93940..c9fc1b1 100644 --- a/server/handlers/vendors/vendorAlist/alist.go +++ b/server/handlers/vendors/vendorAlist/alist.go @@ -114,8 +114,8 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { log := ctx.MustGet("log").(*logrus.Entry) - // Get user and cache data - data, err := s.getUserAndCacheData(ctx) + // Get cache data + data, err := s.getCacheData(ctx) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -133,7 +133,7 @@ func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { } } -func (s *AlistVendorService) getUserAndCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { +func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) if err != nil { return nil, err @@ -154,21 +154,79 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent t := ctx.Query("t") switch t { case "": - ctx.Data(http.StatusOK, "audio/mpegurl", data.Ali.M3U8ListFile) + b, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + if s.movie.Movie.MovieBase.Proxy { + err := proxy.M3u8Data(ctx, b.M3U8ListFile, "", ctx.GetString("token"), s.movie.RoomID, s.movie.ID) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + } + } else { + ctx.Data(http.StatusOK, "audio/mpegurl", b.M3U8ListFile) + } case "raw": - s.proxyURL(ctx, log, data.URL) + b, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + if s.movie.Movie.MovieBase.Proxy { + s.proxyURL(ctx, log, b.URL) + } else { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + return + } case "subtitle": - s.handleSubtitle(ctx, log, data) + s.handleAliSubtitle(ctx, log, data) } } func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { - if !s.movie.Movie.MovieBase.Proxy { - log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) - return + t := ctx.Query("t") + switch t { + case "subtitle": + idS := ctx.Query("id") + if idS == "" { + log.Errorf("proxy vendor movie error: %v", "id is empty") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id is empty")) + return + } + + id, err := strconv.Atoi(idS) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(err)) + return + } + + if id >= len(data.Subtitles) { + log.Errorf("proxy vendor movie error: %v", "id out of range") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + return + } + + subtitle := data.Subtitles[id] + b, err := subtitle.Cache.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + + http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) + default: + if !s.movie.Movie.MovieBase.Proxy { + log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + return + } + s.proxyURL(ctx, log, data.URL) } - s.proxyURL(ctx, log, data.URL) } func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url string) { @@ -186,7 +244,7 @@ func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url s } } -func (s *AlistVendorService) handleSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") @@ -201,20 +259,32 @@ func (s *AlistVendorService) handleSubtitle(ctx *gin.Context, log *logrus.Entry, return } - if id >= len(data.Subtitles) { + ali, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + + var subtitle *cache.AlistSubtitle + if id < len(data.Subtitles) { + subtitle = data.Subtitles[id] + } else if id < len(data.Subtitles)+len(ali.Subtitles) { + subtitle = ali.Subtitles[id-len(data.Subtitles)] + } else { log.Errorf("proxy vendor movie error: %v", "id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) return } - b, err := data.Subtitles[id].Cache.Get(ctx) + b, err := subtitle.Cache.Get(ctx) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - http.ServeContent(ctx.Writer, ctx.Request, data.Subtitles[id].Name, time.Now(), bytes.NewReader(b)) + http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) } func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { @@ -238,18 +308,22 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us return nil, err } - for _, subt := range data.Subtitles { + for i, subt := range data.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: subt.URL, + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), Type: subt.Type, } } switch data.Provider { case cache.AlistProviderAli: + ali, err := data.Ali.Get(ctx) + if err != nil { + return nil, err + } movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) movie.MovieBase.Type = "m3u8" @@ -262,12 +336,12 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us }, } - for i, subt := range data.Subtitles { + for i, subt := range ali.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), Type: subt.Type, } } @@ -314,18 +388,22 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use return nil, err } - for _, subt := range data.Subtitles { + for i, subt := range data.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: subt.URL, + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), Type: subt.Type, } } switch data.Provider { case cache.AlistProviderAli: + ali, err := data.Ali.Get(ctx) + if err != nil { + return nil, err + } movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) movie.MovieBase.Type = "m3u8" @@ -338,12 +416,12 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use }, } - for i, subt := range data.Subtitles { + for i, subt := range ali.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), Type: subt.Type, } } diff --git a/server/handlers/vendors/vendorEmby/emby.go b/server/handlers/vendors/vendorEmby/emby.go index f4c5ca3..6a0f96b 100644 --- a/server/handlers/vendors/vendorEmby/emby.go +++ b/server/handlers/vendors/vendorEmby/emby.go @@ -322,6 +322,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User &dbModel.MoreSource{ Name: es.Name, URL: u.String(), + Type: utils.GetURLExtension(es.URL), }, ) } diff --git a/server/handlers/vendors/vendoralist/alist.go b/server/handlers/vendors/vendoralist/alist.go index 3d93940..c9fc1b1 100644 --- a/server/handlers/vendors/vendoralist/alist.go +++ b/server/handlers/vendors/vendoralist/alist.go @@ -114,8 +114,8 @@ func (s *AlistVendorService) ListDynamicMovie(ctx context.Context, reqUser *op.U func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { log := ctx.MustGet("log").(*logrus.Entry) - // Get user and cache data - data, err := s.getUserAndCacheData(ctx) + // Get cache data + data, err := s.getCacheData(ctx) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) @@ -133,7 +133,7 @@ func (s *AlistVendorService) ProxyMovie(ctx *gin.Context) { } } -func (s *AlistVendorService) getUserAndCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { +func (s *AlistVendorService) getCacheData(ctx *gin.Context) (*cache.AlistMovieCacheData, error) { u, err := op.LoadOrInitUserByID(s.movie.Movie.CreatorID) if err != nil { return nil, err @@ -154,21 +154,79 @@ func (s *AlistVendorService) handleAliProvider(ctx *gin.Context, log *logrus.Ent t := ctx.Query("t") switch t { case "": - ctx.Data(http.StatusOK, "audio/mpegurl", data.Ali.M3U8ListFile) + b, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + if s.movie.Movie.MovieBase.Proxy { + err := proxy.M3u8Data(ctx, b.M3U8ListFile, "", ctx.GetString("token"), s.movie.RoomID, s.movie.ID) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + } + } else { + ctx.Data(http.StatusOK, "audio/mpegurl", b.M3U8ListFile) + } case "raw": - s.proxyURL(ctx, log, data.URL) + b, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + if s.movie.Movie.MovieBase.Proxy { + s.proxyURL(ctx, log, b.URL) + } else { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + return + } case "subtitle": - s.handleSubtitle(ctx, log, data) + s.handleAliSubtitle(ctx, log, data) } } func (s *AlistVendorService) handleDefaultProvider(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { - if !s.movie.Movie.MovieBase.Proxy { - log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") - ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) - return + t := ctx.Query("t") + switch t { + case "subtitle": + idS := ctx.Query("id") + if idS == "" { + log.Errorf("proxy vendor movie error: %v", "id is empty") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id is empty")) + return + } + + id, err := strconv.Atoi(idS) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorResp(err)) + return + } + + if id >= len(data.Subtitles) { + log.Errorf("proxy vendor movie error: %v", "id out of range") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) + return + } + + subtitle := data.Subtitles[id] + b, err := subtitle.Cache.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + + http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) + default: + if !s.movie.Movie.MovieBase.Proxy { + log.Errorf("proxy vendor movie error: %v", "proxy is not enabled") + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("proxy is not enabled")) + return + } + s.proxyURL(ctx, log, data.URL) } - s.proxyURL(ctx, log, data.URL) } func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url string) { @@ -186,7 +244,7 @@ func (s *AlistVendorService) proxyURL(ctx *gin.Context, log *logrus.Entry, url s } } -func (s *AlistVendorService) handleSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { +func (s *AlistVendorService) handleAliSubtitle(ctx *gin.Context, log *logrus.Entry, data *cache.AlistMovieCacheData) { idS := ctx.Query("id") if idS == "" { log.Errorf("proxy vendor movie error: %v", "id is empty") @@ -201,20 +259,32 @@ func (s *AlistVendorService) handleSubtitle(ctx *gin.Context, log *logrus.Entry, return } - if id >= len(data.Subtitles) { + ali, err := data.Ali.Get(ctx) + if err != nil { + log.Errorf("proxy vendor movie error: %v", err) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) + return + } + + var subtitle *cache.AlistSubtitle + if id < len(data.Subtitles) { + subtitle = data.Subtitles[id] + } else if id < len(data.Subtitles)+len(ali.Subtitles) { + subtitle = ali.Subtitles[id-len(data.Subtitles)] + } else { log.Errorf("proxy vendor movie error: %v", "id out of range") ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewAPIErrorStringResp("id out of range")) return } - b, err := data.Subtitles[id].Cache.Get(ctx) + b, err := subtitle.Cache.Get(ctx) if err != nil { log.Errorf("proxy vendor movie error: %v", err) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewAPIErrorResp(err)) return } - http.ServeContent(ctx.Writer, ctx.Request, data.Subtitles[id].Name, time.Now(), bytes.NewReader(b)) + http.ServeContent(ctx.Writer, ctx.Request, subtitle.Name, time.Now(), bytes.NewReader(b)) } func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, userAgent, userToken string) (*dbModel.Movie, error) { @@ -238,18 +308,22 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us return nil, err } - for _, subt := range data.Subtitles { + for i, subt := range data.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: subt.URL, + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), Type: subt.Type, } } switch data.Provider { case cache.AlistProviderAli: + ali, err := data.Ali.Get(ctx) + if err != nil { + return nil, err + } movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) movie.MovieBase.Type = "m3u8" @@ -262,12 +336,12 @@ func (s *AlistVendorService) GenMovieInfo(ctx context.Context, user *op.User, us }, } - for i, subt := range data.Subtitles { + for i, subt := range ali.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), Type: subt.Type, } } @@ -314,18 +388,22 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use return nil, err } - for _, subt := range data.Subtitles { + for i, subt := range data.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: subt.URL, + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), Type: subt.Type, } } switch data.Provider { case cache.AlistProviderAli: + ali, err := data.Ali.Get(ctx) + if err != nil { + return nil, err + } movie.MovieBase.URL = fmt.Sprintf("/api/room/movie/proxy/%s?token=%s&roomId=%s", movie.ID, userToken, movie.RoomID) movie.MovieBase.Type = "m3u8" @@ -338,12 +416,12 @@ func (s *AlistVendorService) GenProxyMovieInfo(ctx context.Context, user *op.Use }, } - for i, subt := range data.Subtitles { + for i, subt := range ali.Subtitles { if movie.MovieBase.Subtitles == nil { movie.MovieBase.Subtitles = make(map[string]*dbModel.Subtitle, len(data.Subtitles)) } movie.MovieBase.Subtitles[subt.Name] = &dbModel.Subtitle{ - URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, i, userToken, movie.RoomID), + URL: fmt.Sprintf("/api/room/movie/proxy/%s?t=subtitle&id=%d&token=%s&roomId=%s", movie.ID, len(data.Subtitles)+i, userToken, movie.RoomID), Type: subt.Type, } } diff --git a/server/handlers/vendors/vendoremby/emby.go b/server/handlers/vendors/vendoremby/emby.go index f4c5ca3..6a0f96b 100644 --- a/server/handlers/vendors/vendoremby/emby.go +++ b/server/handlers/vendors/vendoremby/emby.go @@ -322,6 +322,7 @@ func (s *EmbyVendorService) GenProxyMovieInfo(ctx context.Context, user *op.User &dbModel.MoreSource{ Name: es.Name, URL: u.String(), + Type: utils.GetURLExtension(es.URL), }, ) }