package playletser import ( "fmt" "net/url" "strconv" "strings" "videoweb/app/service/authser" "videoweb/app/service/upstream" "videoweb/pub/common/log" "github.com/gin-gonic/gin" ) const ( chapterDetailPath = "/api/app/playlet/chapter/detail" chapterListPath = "/api/app/playlet/chapter/list" ) // PlayQuery 播放页参数。 type PlayQuery struct { PlayletID int64 PlayletSlug string ChapterID int64 } // ParsePlayQuery 解析播放页的路径参数。 func ParsePlayQuery(playletKey, chapterIDRaw string) PlayQuery { q := PlayQuery{PlayletSlug: strings.TrimSpace(playletKey)} if n, err := strconv.ParseInt(strings.TrimSpace(playletKey), 10, 64); err == nil && n > 0 { q.PlayletID = n q.PlayletSlug = "" } if n, err := strconv.ParseInt(strings.TrimSpace(chapterIDRaw), 10, 64); err == nil && n > 0 { q.ChapterID = n } return q } // FetchChapterDetail 查询剧集播放详情。 func FetchChapterDetail(c *gin.Context, chapterID, playletID int64, playletSlug string) (*PlayletChapterInfoResp, error) { if chapterID <= 0 { return nil, fmt.Errorf("chapter detail invalid chapterId") } params := url.Values{} params.Set("chapterId", strconv.FormatInt(chapterID, 10)) if playletSlug = strings.TrimSpace(playletSlug); playletSlug != "" { params.Set("playletSlug", playletSlug) } else if playletID > 0 { params.Set("playletId", strconv.FormatInt(playletID, 10)) } authser.EnsureGuestOnEnter(c) var env APIEnvelope[PlayletChapterInfoResp] if err := upstream.GetJSON(c, chapterDetailPath+"?"+params.Encode(), &env); err != nil { return nil, err } if env.Code != 200 && authser.IsSessionExpiredCode(env.Code) { if authser.RecoverContentSession(c, env.Code) { if err := upstream.GetJSON(c, chapterDetailPath+"?"+params.Encode(), &env); err != nil { return nil, err } } } if env.Code != 200 { return nil, fmt.Errorf("chapter detail code=%d msg=%s tip=%s", env.Code, env.Msg, env.Tip) } env.Data.SeoInfo.Normalize() return &env.Data, nil } // FetchChapterDetailOrNil 失败返回 nil。 func FetchChapterDetailOrNil(c *gin.Context, chapterID, playletID int64, playletSlug string) *PlayletChapterInfoResp { data, err := FetchChapterDetail(c, chapterID, playletID, playletSlug) if err != nil { log.Error("fetch chapter detail failed", log.E(err)) return nil } if data == nil || data.ID <= 0 { return nil } return data } // FetchChapterList 根据短剧 slug 或 ID 查询选集列表,slug 优先。 func FetchChapterList(c *gin.Context, playletID int64, playletSlug string) (*PlayletChapterListResp, error) { params := url.Values{} if playletSlug = strings.TrimSpace(playletSlug); playletSlug != "" { params.Set("playletSlug", playletSlug) } else if playletID > 0 { params.Set("playletId", strconv.FormatInt(playletID, 10)) } else { return nil, fmt.Errorf("chapter list missing playletSlug or playletId") } authser.EnsureGuestOnEnter(c) var env APIEnvelope[PlayletChapterListResp] if err := upstream.GetJSON(c, chapterListPath+"?"+params.Encode(), &env); err != nil { return nil, err } if env.Code != 200 && authser.IsSessionExpiredCode(env.Code) { if authser.RecoverContentSession(c, env.Code) { if err := upstream.GetJSON(c, chapterListPath+"?"+params.Encode(), &env); err != nil { return nil, err } } } if env.Code != 200 { return nil, fmt.Errorf("playlet chapter list code=%d msg=%s tip=%s", env.Code, env.Msg, env.Tip) } return &env.Data, nil } // ResolvePlayChapterID 无 chapterId 时取短剧第一章。 func ResolvePlayChapterID(playlet *PlayletInfoResp, chapterID int64) int64 { if chapterID > 0 { return chapterID } if playlet == nil { return 0 } for _, ch := range playlet.PlayletChapterInfo { if ch.ID > 0 { return ch.ID } } return 0 } // AllowWatch 是否可播放(优先 canWatch;兼容仅返回 videoUrl 的旧数据)。 func (c *PlayletChapterInfoResp) AllowWatch() bool { if c == nil { return false } if c.CanWatch { return true } if c.NeedUnlock || c.NeedVip { return false } return strings.TrimSpace(c.VideoURL) != "" && c.PayType == 0 } // AllowWatch 详情选集是否可直达播放(优先 canWatch)。 func (c PlayletChapterData) AllowWatch() bool { if c.CanWatch { return true } if c.NeedUnlock || c.NeedVip { return false } if c.IsUnlocked { return true } return c.PayType == 0 }