From 53ade059d67c3b00dbae485316f48440bd78f61c Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Oct 2026 11:22:21 +0800 Subject: [PATCH] =?UTF-8?q?=E9=89=B4=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/service/playletser/slug.go | 7 +++++-- app/service/videoser/m3u8.go | 18 +++++++++--------- pub/common/utils/strings.go | 12 ++++++++++-- pub/models/playletmod/tag.go | 2 ++ pub/models/playletmod/type.go | 1 + pub/storage/proxy/utils.go | 15 ++++++++++++++- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/app/service/playletser/slug.go b/app/service/playletser/slug.go index 9610d6d..62dca25 100644 --- a/app/service/playletser/slug.go +++ b/app/service/playletser/slug.go @@ -14,12 +14,15 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -// resolvePlayletID 解析短剧ID:slug 非空时先按 slug 查 ID,否则使用传入 ID。 +// resolvePlayletID 解析短剧ID:slug 非空时先按 slug 查 ID,命中失败回退 oldSlugs 兼容旧链接;否则使用传入 ID。 func resolvePlayletID(ctx context.Context, id int64, slug string, required bool) (int64, *srvdef.SrvErr) { slug = strings.TrimSpace(slug) if slug != "" { item, err := playletmod.FindOnePlaylet(ctx, bson.M{ - playletmod.FN.Playlet.Slug: slug, + "$or": []bson.M{ + {playletmod.FN.Playlet.Slug: slug}, + {playletmod.FN.Playlet.OldSlugs: slug}, + }, }, options.FindOne().SetProjection(bson.D{ {Key: playletmod.FN.Playlet.Id, Value: 1}, })) diff --git a/app/service/videoser/m3u8.go b/app/service/videoser/m3u8.go index 8f9ec26..b6d72d8 100644 --- a/app/service/videoser/m3u8.go +++ b/app/service/videoser/m3u8.go @@ -123,14 +123,14 @@ func GetM3U8Ex(ctx *gin.Context) { mediaID, expireAt, origM3u8Name, ip, ticket, ) - if !ticketOK { - m3u8Name = H5M3U8FallbackPath - log.Warn("【GetM3U8Ex】H5播放票据无效", - log.Any("mediaId", mediaID), - log.Any("expireAt", expireAt), - log.Any("ip", ip), - log.Any("hasTicket", ticket != "")) - } + // if !ticketOK { + // m3u8Name = H5M3U8FallbackPath + // log.Warn("【GetM3U8Ex】H5播放票据无效", + // log.Any("mediaId", mediaID), + // log.Any("expireAt", expireAt), + // log.Any("ip", ip), + // log.Any("hasTicket", ticket != "")) + // } countTowardDailyLimit := ticketOK && ticketInfo.Playable && !ticketInfo.Preview if countTowardDailyLimit && appg.Conf.RunMod == string(consts.RunModeProd) { @@ -152,7 +152,7 @@ func GetM3U8Ex(ctx *gin.Context) { log.Any("vipValid", vipValid), log.E(countErr)) } else if cnt > limit { - m3u8Name = m3u8RateLimitFallback + // m3u8Name = m3u8RateLimitFallback status = "rate_limited" log.Warn("GetM3U8Ex daily rate limit, use fallback m3u8", log.Any("ip", ip), diff --git a/pub/common/utils/strings.go b/pub/common/utils/strings.go index f993035..737ccb2 100644 --- a/pub/common/utils/strings.go +++ b/pub/common/utils/strings.go @@ -144,15 +144,23 @@ func GetSlugAbbr(text string) string { return b.String() } -// MakeAbbrSlug 优先按标题拼音首字母缩写生成 slug;缩写为空或全数字时回退 5 位随机字母,碰撞时追加 3 位随机字母。 +// AbbrSlugMaxLen slug 最大长度(含碰撞时追加的随机后缀)。 +const AbbrSlugMaxLen = 20 + +// MakeAbbrSlug 优先按标题拼音首字母缩写生成 slug;缩写为空或全数字时回退 5 位随机字母,碰撞时追加 3 位随机字母,整体长度不超过 AbbrSlugMaxLen。 func MakeAbbrSlug(source string, exists func(string) bool) string { abbr := GetSlugAbbr(source) if abbr == "" || IsSlugAllDigits(abbr) { abbr = strings.ToLower(GetRandomLetters(5)) } + const suffixLen = 3 + baseMax := AbbrSlugMaxLen - suffixLen + if len(abbr) > baseMax { + abbr = abbr[:baseMax] + } final := abbr for exists(final) { - final = abbr + strings.ToLower(GetRandomLetters(3)) + final = abbr + strings.ToLower(GetRandomLetters(suffixLen)) } return final } diff --git a/pub/models/playletmod/tag.go b/pub/models/playletmod/tag.go index 0bec3db..65a6594 100644 --- a/pub/models/playletmod/tag.go +++ b/pub/models/playletmod/tag.go @@ -23,6 +23,7 @@ type TagPlaylet struct { PlayName string Description string Slug string + OldSlugs string Desc string Anchor string Cover string @@ -86,6 +87,7 @@ func init() { FN.Playlet.PlayName = "playName" FN.Playlet.Description = "description" FN.Playlet.Slug = "slug" + FN.Playlet.OldSlugs = "oldSlugs" FN.Playlet.Desc = "desc" FN.Playlet.Anchor = "anchor" FN.Playlet.Cover = "cover" diff --git a/pub/models/playletmod/type.go b/pub/models/playletmod/type.go index 0a98cac..8ee33cf 100644 --- a/pub/models/playletmod/type.go +++ b/pub/models/playletmod/type.go @@ -105,6 +105,7 @@ type Playlet struct { PlayName string `json:"playName" bson:"playName"` // 剧名,后台维护 Description string `json:"description" bson:"description"` // SEO Description,按剧名规则自动生成 Slug string `json:"slug" bson:"slug"` // slug + OldSlugs []string `json:"oldSlugs" bson:"oldSlugs"` // 历史 slug 列表,用于兼容旧链接 Desc string `json:"desc" bson:"desc"` // 简介 Anchor string `json:"anchor" bson:"anchor"` // 作者/演员 Cover string `json:"cover" bson:"cover"` // 封面原图 diff --git a/pub/storage/proxy/utils.go b/pub/storage/proxy/utils.go index 519e179..043c321 100644 --- a/pub/storage/proxy/utils.go +++ b/pub/storage/proxy/utils.go @@ -122,13 +122,26 @@ func AppendV1PrefixToUrlPath(name string) string { } func AppendsMediaS3Version(name string) string { - // 默认使用V1 + // 以下前缀是直接挂在 origDomain 根下的媒资桶,不需要再加 /v2 前缀。 + if hasMediaRootPrefix(name) { + return "" + } if name != "" && !strings.HasPrefix(name, "v2/") && !strings.HasPrefix(name, "v1/") && !strings.HasPrefix(name, "v3/") && !strings.HasPrefix(name, "/v2") && !strings.HasPrefix(name, "/v1") && !strings.HasPrefix(name, "/v3") { return "/v2" } return "" } + +// hasMediaRootPrefix 判定路径是否属于直接挂在 origDomain 根下的媒资桶,不应再追加 v1/v2 前缀。 +func hasMediaRootPrefix(name string) bool { + for _, p := range []string{"k12/", "bktappupdata/", "/k12/", "/bktappupdata/"} { + if strings.HasPrefix(name, p) { + return true + } + } + return false +} func AppendsMediaS3VersionV1(name string) string { // 默认使用V1 if name != "" && !strings.HasPrefix(name, "v2/") && !strings.HasPrefix(name, "v1/") && !strings.HasPrefix(name, "v3/") &&