Files
web_hgdjc_server/app/service/videoser/vtt.go
T
2026-11-07 10:44:09 +08:00

159 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package videoser
import (
"bufio"
"bytes"
"fmt"
"io"
http2 "net/http"
"strings"
"videoweb/app/appg"
"videoweb/app/service/configser"
"videoweb/pub/common"
"videoweb/pub/common/enum/redisconst"
"videoweb/pub/common/errors"
"videoweb/pub/common/http"
"videoweb/pub/common/log"
"videoweb/pub/common/m3u8"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/redis"
"videoweb/pub/storage/proxy"
"github.com/gin-gonic/gin"
)
func GetVTTEx(ctx *gin.Context) {
fn := func(c *gin.Context) (contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string, err error) {
vttName := ctx.Param("key")
var req m3u8.Req
if err = ctx.BindQuery(&req); err != nil {
return
}
req.Name = vttName
cfg := configser.GetConfigInfo()
cdnDomain := cfg.ImgCdn
originDomain := appg.Conf.OrigDomain
buff, err := TransformVttEx(req.Name, cdnDomain, originDomain)
if err != nil {
return
}
contentType = "application/vnd.apple.mpegurl"
reader = bytes.NewReader(buff)
contentLength = int64(len(buff))
extraHeaders = map[string]string{}
return
}
contentLength, contentType, reader, extraHeaders, err := fn(ctx)
if err != nil {
common.ServerJSON(ctx, srvdef.DownloadM3u8File, err)
return
}
ctx.DataFromReader(http2.StatusOK, contentLength, contentType, reader, extraHeaders)
}
func TransformVttEx(name, cdnDomain, originDomain string) (buff []byte, err error) {
if name == "" {
err = errors.MakeWithMsg(errors.M3u8FileName, "m3u8 file name is empty")
return
}
if cdnDomain == "" || originDomain == "" {
err = errors.MakeWithMsg(errors.DomainErr, "cdnDomain or origDomain is empty")
return
}
// 处理媒资库 桶1(v1) 桶2(v2) 桶3(v3)
mediaVerson := proxy.AppendsMediaS3Version(name)
if len(mediaVerson) > 0 {
name = mediaVerson + name
}
// 原始vtt
srcReader, err := readFromRedis(name, originDomain)
if err != nil {
return
}
raw, err := io.ReadAll(srcReader)
if err != nil {
return nil, errors.MakeWithMsg(errors.M3u8Read, "read vtt reader fail: "+err.Error())
}
// 替换为cdn路径
newData := rewriteVttWithCDN(raw, cdnDomain)
reader := bytes.NewReader(newData)
buff, err = io.ReadAll(reader)
if err != nil {
return nil, errors.MakeWithMsg(errors.M3u8Read, "read vtt fail: "+err.Error())
}
return
}
// 从redis缓存中读取
func readFromRedis(name, domain string) (reader io.Reader, err error) {
vttKey := redisconst.MediaM3U8Key(name)
data, err := redis.GetBytes(vttKey)
if err == nil && len(data) > 0 {
reader = bytes.NewReader(data)
return
}
requestUrl := utils.JoinUrlPath(domain, name)
resp, err := http.GetLargeFile(requestUrl, nil)
if err != nil {
fmt.Println("requestUrl111", requestUrl)
return
}
fmt.Println("requestUrl", requestUrl, resp.StatusCode)
if resp.StatusCode != 200 {
msg := fmt.Sprintf("read vtt fail, url: %s, status code %d", requestUrl, resp.StatusCode)
err = errors.MakeWithMsg(errors.M3u8Read, msg)
return
}
if err := redis.Set(vttKey, resp.Data,
redisconst.MediaM3U8CacheExpire); err != nil {
log.Info("vtt:readFromRedis redis set err", log.E(err))
}
reader = bytes.NewReader(resp.Data)
return
}
// rewriteVttWithCDN 将 vtt 内 "path#xywh=..." 的 path 改为 cdnDomain/path
func rewriteVttWithCDN(vttData []byte, cdnDomain string) []byte {
cdnDomain = strings.TrimSpace(cdnDomain)
cdnDomain = strings.TrimRight(cdnDomain, "/")
var out bytes.Buffer
scanner := bufio.NewScanner(bytes.NewReader(vttData))
for scanner.Scan() {
line := scanner.Text()
trim := strings.TrimSpace(line)
// 只处理缩略图行:xxx#xywh=...
if strings.Contains(trim, "#xywh=") {
parts := strings.SplitN(trim, "#xywh=", 2)
if len(parts) == 2 {
p := strings.TrimSpace(parts[0]) // 原路径
xywh := parts[1]
// 已经是绝对URL则不改
if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
out.WriteString(p + "#xywh=" + xywh + "\n")
continue
}
// 处理相对路径,确保只有一个 /
p = strings.TrimLeft(p, "/")
newPath := cdnDomain + "/" + p
out.WriteString(newPath + "#xywh=" + xywh + "\n")
continue
}
}
out.WriteString(line + "\n")
}
// scanner.Err()
return out.Bytes()
}