This commit is contained in:
2026-07-16 14:56:06 +08:00
parent 8c353b12be
commit d18fd711cd
3 changed files with 73 additions and 15 deletions
BIN
View File
Binary file not shown.
+72 -10
View File
@@ -392,7 +392,7 @@ func (s *dramaService) generateOneSegment(ctx context.Context, d *entity.Drama,
// JSON 镜头脚本跳过 Agent,直接构建场景描述提交视频模型
var segOutput *model.SegmentOutput
if domain.IsShotsJSON(ep.Script) && feedback == "" {
segOutput = buildSegOutputFromShots(ep.Script, segIdx, segStartTime, segDur)
segOutput = buildSegOutputFromShots(ep.Script, segIdx, segStartTime, segDur, genCtx)
g.Log().Infof(ctx, "第%d集第%d段 JSON镜头直接提交: %d个镜头", ep.Index, segIdx+1, len(segOutput.Scenes))
} else {
result, err := s.generateSegment(ctx, d, ep, segIdx, segDur, segStartTime, totalSegs, modelCfg, feedback, genCtx)
@@ -556,7 +556,7 @@ func (s *dramaService) generateOneSegment(ctx context.Context, d *entity.Drama,
// buildSegOutputFromShots 从 JSON 镜头数组直接构建 SegmentOutput(跳过 Agent
// 将本段所有镜头合并为一段连贯的场景描述,去掉"旁白/台词/事件"标记,适合视频模型理解
func buildSegOutputFromShots(script string, segIdx, segStartTime, segDur int) *model.SegmentOutput {
func buildSegOutputFromShots(script string, segIdx, segStartTime, segDur int, genCtx *GenerationContext) *model.SegmentOutput {
var allShots []domain.Shot
if err := json.Unmarshal([]byte(script), &allShots); err != nil || len(allShots) == 0 {
return &model.SegmentOutput{Index: segIdx, TextOutput: script}
@@ -584,23 +584,80 @@ func buildSegOutputFromShots(script string, segIdx, segStartTime, segDur int) *m
chars = append(chars, model.SegmentCharacter{Name: name})
}
// 将所有镜头合并为一段连贯描述(去掉旁白/台词/事件标记,自然语言拼接
// 从 genCtx 构建角色外观描述(确保每段角色服装/外貌一致
var appearanceDescs []string
for name := range charSet {
for _, c := range genCtx.Characters {
if c.Name == name && c.Description != "" {
appearanceDescs = append(appearanceDescs, name+""+c.Description)
break
}
}
}
// 收集本段涉及场景的描述(确保各段画风一致)
var sceneDescs []string
seenScenes := make(map[string]bool)
for _, sh := range segShots {
if sh.Scene == "" || seenScenes[sh.Scene] {
continue
}
seenScenes[sh.Scene] = true
for _, sc := range genCtx.Scenes {
if sc.Name == sh.Scene && sc.Description != "" {
sceneDescs = append(sceneDescs, sc.Name+""+sc.Description)
break
}
}
}
// 将所有镜头合并为一段连贯描述,每个镜头包含场景/运镜/道具信息
descParts := make([]string, 0, len(segShots)*2)
for _, sh := range segShots {
// 旁白直接作为叙述
// 镜头头部信息:场景、运镜、道具
var shotHeaders []string
if sh.Scene != "" {
shotHeaders = append(shotHeaders, "场景:"+sh.Scene)
}
if sh.CameraMovement != "" {
shotHeaders = append(shotHeaders, "运镜:"+sh.CameraMovement)
}
if len(sh.Props) > 0 {
shotHeaders = append(shotHeaders, "道具:"+strings.Join(sh.Props, "、"))
}
shotParts := make([]string, 0, 4)
if len(shotHeaders) > 0 {
shotParts = append(shotParts, "["+strings.Join(shotHeaders, "][")+"]")
}
// 旁白
if sh.Narration != "" {
descParts = append(descParts, sh.Narration)
shotParts = append(shotParts, sh.Narration)
}
// 事件描述
if sh.Event != "" {
descParts = append(descParts, sh.Event)
shotParts = append(shotParts, sh.Event)
}
// 对话以自然方式嵌入
if sh.Dialogue != "" {
descParts = append(descParts, sh.Dialogue)
shotParts = append(shotParts, sh.Dialogue)
}
if len(shotParts) > 0 {
descParts = append(descParts, strings.Join(shotParts, "。"))
}
}
fullDesc := strings.Join(descParts, "")
// 前面拼接角色外观和场景描述,确保风格一致
var prefixParts []string
if len(appearanceDescs) > 0 {
prefixParts = append(prefixParts, "【角色形象】"+strings.Join(appearanceDescs, ""))
}
if len(sceneDescs) > 0 {
prefixParts = append(prefixParts, "【场景】"+strings.Join(sceneDescs, ""))
}
var fullDesc string
if len(prefixParts) > 0 {
fullDesc = strings.Join(prefixParts, "。") + "。" + strings.Join(descParts, "")
} else {
fullDesc = strings.Join(descParts, "")
}
return &model.SegmentOutput{
Index: segIdx,
@@ -1460,8 +1517,10 @@ func (s *dramaService) submitVideoTask(ctx context.Context, d *entity.Drama, ep
}
}
// 固定 seed 确保各段画风/人物形象一致(基于短剧ID+剧集序号)
seed := int(d.Id)*1000 + ep.Index*10 + segIdx
taskId, requestJSON, err := createVideoTask(ctx, modelCfg.VideoApiKey, modelCfg.VideoBaseUrl, modelCfg.VideoModelName,
prompt, negativePrompt, refURLs, effectiveDur, d.Resolution, d.AspectRatio, modelCfg.VideoSchema)
prompt, negativePrompt, refURLs, effectiveDur, d.Resolution, d.AspectRatio, modelCfg.VideoSchema, seed)
if err != nil {
return "", "", fmt.Errorf("视频合成请求失败: %w", err)
}
@@ -1478,7 +1537,7 @@ func (s *dramaService) submitVideoTask(ctx context.Context, d *entity.Drama, ep
//
// params: 额外参数(如 audio/shot_type/watermark 等)
// sizes: 分辨率→尺寸字符串映射表
func createVideoTask(ctx context.Context, apiKey, baseURL, modelName, prompt, negativePrompt string, refURLs []string, duration int, resolution, aspectRatio, videoSchema string) (string, string, error) {
func createVideoTask(ctx context.Context, apiKey, baseURL, modelName, prompt, negativePrompt string, refURLs []string, duration int, resolution, aspectRatio, videoSchema string, seed int) (string, string, error) {
body := map[string]any{
"model": modelName,
"input": map[string]any{
@@ -1538,6 +1597,9 @@ func createVideoTask(ctx context.Context, apiKey, baseURL, modelName, prompt, ne
if duration > 0 {
params["duration"] = duration
}
if seed > 0 {
params["seed"] = seed
}
body["parameters"] = params
payload, _ := json.Marshal(body)
+1 -5
View File
@@ -469,14 +469,11 @@ func createPendingTasks(ctx context.Context, dramaId, epId int64, script string,
totalDur int
}
var cur _shotGroup
var prevScene string
for _, sh := range allShots {
shDur := parseMMSSToSeconds(sh.EndTime) - parseMMSSToSeconds(sh.StartTime)
if shDur <= 0 {
shDur = 1
}
// 场景切换 → 必须分段(不同场景不能合并到同一段)
sceneChanged := len(cur.shots) > 0 && sh.Scene != prevScene
// 计算加入当前镜头后的候选组总时长和提示词长度
candShots := make([]domain.Shot, len(cur.shots)+1)
copy(candShots, cur.shots)
@@ -487,7 +484,7 @@ func createPendingTasks(ctx context.Context, dramaId, epId int64, script string,
exceedDur := len(cur.shots) > 0 && candDur > effectiveMax
exceedChars := promptMaxChars > 0 && len([]rune(candPrompt)) > promptMaxChars
if exceedDur || exceedChars || sceneChanged {
if exceedDur || exceedChars {
// 当前组已满,保存并开启新组
taskGroups = append(taskGroups, _taskGroup{
promptText: buildFinalPrompt(domain.ShotsToText(cur.shots)),
@@ -498,7 +495,6 @@ func createPendingTasks(ctx context.Context, dramaId, epId int64, script string,
cur.shots = candShots
cur.totalDur = candDur
}
prevScene = sh.Scene
}
if len(cur.shots) > 0 {
taskGroups = append(taskGroups, _taskGroup{