Initial commit

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 11:07:42 +08:00
co-authored by Claude Opus 5.5
commit 7974a42301
633 changed files with 120248 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
package srv_videoinfo
import (
"context"
"fmt"
"strings"
"videoweb/pub/common/laosiji"
"videoweb/pub/common/log"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/videoinformationmod"
"videoweb/web/webg"
"go.mongodb.org/mongo-driver/bson"
)
// TranscodeVideo 手动提交主视频 H265 转码。
func TranscodeVideo(ctx context.Context, videoId int64) *srvdef.SrvErr {
info, serr := videoinformationmod.FindOneVideoInformation(ctx, bson.M{
videoinformationmod.FN.VideoInformation.Id: videoId,
})
if serr != nil {
return serr
}
src := strings.TrimSpace(info.Video.Url)
if src == "" {
return srvdef.New(srvdef.Err, "", "该视频没有可转码的源地址")
}
if laosiji.LaosijiClient == nil || laosiji.LaosijiDomains == nil {
return srvdef.New(srvdef.Err, "", "转码服务未就绪")
}
if info.HevcStatus == videoinformationmod.HevcStatusPending {
return srvdef.New(srvdef.Err, "", "转码请求已提交,正在转码中,请勿重复提交")
}
if err := SubmitTranscodeVideo(ctx, videoId, src, info.Video.FileId); err != nil {
return srvdef.New(srvdef.Err, "", "提交转码失败:"+err.Error())
}
return nil
}
func buildTranscodeSourceURL(videoUrl string) string {
name := strings.TrimSpace(videoUrl)
if name == "" {
return ""
}
if !strings.Contains(name, ".m3u8") {
name += ".m3u8"
}
host := strings.TrimSpace(webg.Conf.WebM3u8Host)
if host == "" {
return ""
}
return utils.JoinUrlPath(host, "/api/web/m3u8/"+name)
}
// SubmitTranscodeVideo 提交主视频转码任务。
func SubmitTranscodeVideo(ctx context.Context, videoId int64, src, fileId string) error {
fileURL := buildTranscodeSourceURL(src)
if fileURL == "" {
return fmt.Errorf("empty transcode source url, videoUrl:%s host:%s", src, webg.Conf.WebM3u8Host)
}
sd, err := laosiji.LaosijiDomains.Get(ctx)
if err != nil {
return err
}
result, err := laosiji.LaosijiClient.SubmitTranscoding(ctx, fileURL, fileId, *sd, videoId)
if err != nil {
return err
}
return applyTranscodeResult(ctx, videoId, result)
}
// RefreshTranscodeVideo 查询转码任务状态并回写。
func RefreshTranscodeVideo(ctx context.Context, videoId int64, src, fileId string) error {
fileURL := buildTranscodeSourceURL(src)
if fileURL == "" {
return fmt.Errorf("empty transcode source url, videoUrl:%s host:%s", src, webg.Conf.WebM3u8Host)
}
sd, err := laosiji.LaosijiDomains.Get(ctx)
if err != nil {
return err
}
result, found, err := laosiji.LaosijiClient.QueryTranscoding(ctx, fileURL, fileId, *sd)
if err != nil {
return err
}
if !found {
return nil
}
return applyTranscodeResult(ctx, videoId, result)
}
func applyTranscodeResult(ctx context.Context, videoId int64, result laosiji.TranscodeResult) error {
log.Infof("==applyTranscodeResult===> result", log.Any("videoId", videoId), log.Any("result", result))
var update bson.M
switch {
case result.Done:
meta := toVideoMetaInfo(result.HevcMeta)
if meta == nil {
meta = &videoinformationmod.VideoMetaInfo{}
}
h265Url := normalizeH265UrlForSave(result.HevcURL)
meta.M3u8URL = h265Url
set := bson.M{
videoinformationmod.FN.VideoInformation.HevcStatus: videoinformationmod.HevcStatusSuccess,
videoinformationmod.FN.VideoInformation.HevcMetaInfo: meta,
videoinformationmod.FN.VideoInformation.Video.H265Url: h265Url,
}
if result.PreviewVtt != "" {
set[videoinformationmod.FN.VideoInformation.PreviewVtt] = result.PreviewVtt
}
if result.AudioFile != "" {
set[videoinformationmod.FN.VideoInformation.AudioFile] = result.AudioFile
}
if result.SmartCover != "" {
set[videoinformationmod.FN.VideoInformation.SmartCover] = result.SmartCover
}
update = bson.M{"$set": set}
case result.Failed:
update = bson.M{
"$set": bson.M{videoinformationmod.FN.VideoInformation.HevcStatus: videoinformationmod.HevcStatusFailed},
"$inc": bson.M{videoinformationmod.FN.VideoInformation.HevcFailCount: 1},
}
default:
update = bson.M{"$set": bson.M{videoinformationmod.FN.VideoInformation.HevcStatus: videoinformationmod.HevcStatusPending}}
}
if serr := videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{
videoinformationmod.FN.VideoInformation.Id: videoId,
}, update); serr != nil {
return fmt.Errorf("update video transcode status: %v", serr)
}
return nil
}
func normalizeH265UrlForSave(url string) string {
url = strings.TrimSpace(url)
return strings.TrimPrefix(url, "/")
}
func toVideoMetaInfo(m *laosiji.MetaInfo) *videoinformationmod.VideoMetaInfo {
if m == nil {
return nil
}
return &videoinformationmod.VideoMetaInfo{
Codec: m.Codec,
Profile: m.Profile,
Width: m.Width,
Height: m.Height,
Fps: m.Fps,
VideoBitrate: m.VideoBitrate,
PixFmt: m.PixFmt,
AudioCodec: m.AudioCodec,
AudioBitrate: m.AudioBitrate,
AudioSampleRate: m.AudioSampleRate,
AudioChannels: m.AudioChannels,
Bitrate: m.Bitrate,
Size: m.Size,
Duration: m.Duration,
}
}