Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
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 (
|
||
detailPath = "/api/app/playlet/detail"
|
||
defaultEpisodeRange = 30
|
||
)
|
||
|
||
// EpisodeRange 剧集分段(仅 UI 分页展示,非接口字段映射)。
|
||
type EpisodeRange struct {
|
||
Start int
|
||
End int
|
||
Label string
|
||
}
|
||
|
||
// FetchDetail 根据短剧 ID 查询详情。
|
||
func FetchDetail(c *gin.Context, id int64) (*PlayletInfoResp, error) {
|
||
if id <= 0 {
|
||
return nil, fmt.Errorf("playlet detail invalid id")
|
||
}
|
||
return fetchDetail(c, "id", strconv.FormatInt(id, 10))
|
||
}
|
||
|
||
// FetchDetailByKey 优先按 slug 查询;纯数字路由段按 ID 查询。
|
||
func FetchDetailByKey(c *gin.Context, key string) (*PlayletInfoResp, error) {
|
||
key = strings.TrimSpace(key)
|
||
if key == "" {
|
||
return nil, fmt.Errorf("playlet detail missing route key")
|
||
}
|
||
if id := ParseDetailID(key); id > 0 {
|
||
return FetchDetail(c, id)
|
||
}
|
||
return fetchDetail(c, "slug", key)
|
||
}
|
||
|
||
func fetchDetail(c *gin.Context, queryKey, value string) (*PlayletInfoResp, error) {
|
||
authser.EnsureGuestOnEnter(c)
|
||
env, err := fetchDetailOnce(c, queryKey, value)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if env.Code != 200 && authser.IsSessionExpiredCode(env.Code) {
|
||
if authser.RecoverContentSession(c, env.Code) {
|
||
env, err = fetchDetailOnce(c, queryKey, value)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
if env.Code != 200 {
|
||
return nil, fmt.Errorf("playlet detail code=%d msg=%s tip=%s", env.Code, env.Msg, env.Tip)
|
||
}
|
||
env.Data.SeoInfo.Normalize()
|
||
return &env.Data, nil
|
||
}
|
||
|
||
func fetchDetailOnce(c *gin.Context, queryKey, value string) (*APIEnvelope[PlayletInfoResp], error) {
|
||
params := url.Values{}
|
||
params.Set(queryKey, value)
|
||
var env APIEnvelope[PlayletInfoResp]
|
||
if err := upstream.GetJSON(c, detailPath+"?"+params.Encode(), &env); err != nil {
|
||
return nil, err
|
||
}
|
||
return &env, nil
|
||
}
|
||
|
||
// FetchDetailOrNilByKey 失败或无效时返回 nil。
|
||
func FetchDetailOrNilByKey(c *gin.Context, key string) *PlayletInfoResp {
|
||
data, err := FetchDetailByKey(c, key)
|
||
if err != nil {
|
||
log.Error("fetch playlet detail by route key failed", log.E(err))
|
||
return nil
|
||
}
|
||
if data == nil || data.ID <= 0 {
|
||
return nil
|
||
}
|
||
return data
|
||
}
|
||
|
||
// ParseDetailID 解析纯数字路由段。
|
||
func ParseDetailID(raw string) int64 {
|
||
id, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
|
||
if err != nil || id <= 0 {
|
||
return 0
|
||
}
|
||
return id
|
||
}
|
||
|
||
// BuildEpisodeRanges 按集数分段(默认每段 30 集)。
|
||
func BuildEpisodeRanges(chapters []PlayletChapterData, totalEpisode, size int) []EpisodeRange {
|
||
if size <= 0 {
|
||
size = defaultEpisodeRange
|
||
}
|
||
maxEp := 0
|
||
for _, ch := range chapters {
|
||
if ch.CurrentEpisode > maxEp {
|
||
maxEp = ch.CurrentEpisode
|
||
}
|
||
}
|
||
if totalEpisode > maxEp {
|
||
maxEp = totalEpisode
|
||
}
|
||
if maxEp <= 0 {
|
||
maxEp = len(chapters)
|
||
}
|
||
if maxEp <= 0 {
|
||
return nil
|
||
}
|
||
out := make([]EpisodeRange, 0, (maxEp+size-1)/size)
|
||
for start := 1; start <= maxEp; start += size {
|
||
end := start + size - 1
|
||
if end > maxEp {
|
||
end = maxEp
|
||
}
|
||
out = append(out, EpisodeRange{
|
||
Start: start,
|
||
End: end,
|
||
Label: fmt.Sprintf("%d–%d", start, end),
|
||
})
|
||
}
|
||
return out
|
||
}
|