来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83 项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet 测试目录 /server/hkdj_net,App/Web API 30331/30332 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
161 lines
5.0 KiB
Go
161 lines
5.0 KiB
Go
package srv_videoinfo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"hkdj-web-server/pub/common/laosiji"
|
|
"hkdj-web-server/pub/common/log"
|
|
"hkdj-web-server/pub/common/srvdef"
|
|
"hkdj-web-server/pub/common/utils"
|
|
"hkdj-web-server/pub/models/videoinformationmod"
|
|
"hkdj-web-server/web/webg"
|
|
"strings"
|
|
|
|
"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,
|
|
}
|
|
}
|