From c91aba64f5b8e736ab9c976e4f4f8376a152f3ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Fri, 24 Jul 2026 13:24:00 +0800 Subject: [PATCH] 1 --- common/base_dao.go | 31 ------------------ short_drama.db | Bin 462848 -> 462848 bytes shortdrama/{config => agent}/shot_duration.go | 2 +- shortdrama/dao/background_music_dao.go | 27 +++++++++++++-- shortdrama/dao/character_dao.go | 27 +++++++++++++-- shortdrama/dao/episode_dao.go | 3 +- shortdrama/dao/prop_dao.go | 27 +++++++++++++-- shortdrama/dao/scene_dao.go | 27 +++++++++++++-- shortdrama/service/episode_service.go | 6 ++-- 9 files changed, 105 insertions(+), 45 deletions(-) rename shortdrama/{config => agent}/shot_duration.go (99%) diff --git a/common/base_dao.go b/common/base_dao.go index 92706bc..6d0b604 100644 --- a/common/base_dao.go +++ b/common/base_dao.go @@ -50,34 +50,3 @@ func DeleteByPk(ctx context.Context, table string, pk int64) error { _, err := g.DB().Model(table).Ctx(ctx).Unscoped().Where("id", pk).Delete() return err } - -func DeleteByDrama(ctx context.Context, table string, dramaId int64) error { - _, err := g.DB().Model(table).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() - return err -} - -func ListPageByDrama[T any](ctx context.Context, table string, dramaId int64, page, pageSize int) (res []*T, total int, err error) { - m := g.DB().Model(table).Ctx(ctx).Where("drama_id", dramaId) - if pageSize == -1 { - r, err := m.OrderAsc("id").All() - if err != nil { - return nil, 0, err - } - res = make([]*T, 0) - err = r.Structs(&res) - return res, len(res), err - } - if page < 1 { - page = 1 - } - if pageSize < 1 { - pageSize = 20 - } - r, total, err := m.OrderAsc("id").Limit(pageSize).Offset((page - 1) * pageSize).AllAndCount(false) - if err != nil { - return - } - res = make([]*T, 0) - err = r.Structs(&res) - return -} diff --git a/short_drama.db b/short_drama.db index 59a4dc98c67a84964fe74a841403b78c65aa1ccb..02e8a3077f8286f3daa5ac0fc7bfcdc5357caa44 100644 GIT binary patch delta 4928 zcmeHLdr(y86=(0=@26V^X{TwUb-(*aG7)1m2`t!icYzRN9U0sI zGIMulcJKYpch32K=XZYRnp%@gtx0uDRpa)g@2ST7cCOD->CPO@dZI2|RS3U z3w0O0HBx0i9X=uVHw%ZYy!p*d@RrP`L+>_{3=f}Y@8I8hWmQJrighb?J-2JmGb@T# z2%TQ3Vu)J1gbE)mE0vC)6dHzPQ>#!>=XaNZ#=)*(dV9g(V3%fyM^DM%l*+qlRhLvZ zEH{{OLu#|ra<|az7LGe)=%IBLwB5m*Up0dMGMfn6t8500mT&~BM%ZW={*2W^QXd-| z8+2mN&mVkh_bzlpuP@RUz~S@kGIgtLGV?nEnIPdW;wu-LTF_kXp{wdeVEZ+b4!#nO zn`6y)f{kJH1-#=A$uOaqzyA`)ZNNb2V2L<<8Uw*p_&x1%X*~ujw++y~0~bsO{9dQr zXcJpZ)YU~hUKSfI;&H3gegM74sNj4*yZAw|bx=Iv6Pmjur;;CBi?`(RTD$~%18wdS z>}7(d4J}KyX0#x*)zF?g|6mn9hO20woz~YV`#PMosY!5`;=Zu`0$Uu)4Ia(+VUY_@ zCGoZV@A^Gf<LLVD(V^DJEm{s5*u>y&kK|gLQs3i(ma}48LSbo1@^Q%tg<5&?L2;5h|;N zgHCz44owO*l^B{-ipS~1plF3jIA%eo(EKLlJQHBi0Zt3YM0O4AzQpD8SEte-V}RAg zqb;eeODHK7oi9^wE5)!Kr})0tw!_6vb}fuvz<57kHLIX`2wnS&z{Yd5yoOpU!M2}C z;RCO4Qyz@FeiOLD#ljIco5uh9jnz78>z0oUh`bB4McW5vf= V^m_3mB^zY^+Prm z?)0)T;Jw1dY@&{`&;qfci&iz^(b77*-0+gr)=q0rDxMFRY44D5Xh?=`+3ZvP89pVs zPfD&51i07@wBry~)URNXMiZ%kEEyjEh>eTQRjO$&kh2TW1rZ5>j3so!^6?sUv9_q*NV zNQZy0M`-n;M?$3)5fMzn;R|fhSCtOG8Aj&{uXirT097Bc-{Ej8+00|g6JhcrR;LaN z3wxmd9d#

OSE$V`+@;y|bC*J`Qh$TfoGTy)pG#i#9qeT(w|n)aK1mQL5v+LH`_c z$6Yx^hTNhY{auB51qMU@T75y0UZ1Vc&db#2XJ#8y4PU!oz`uKC3G8&L*PXfVa;nPJ zDw6dK?5HBqI^#ue<&HgmceUSxb=Jt+M$`D;j&Hwpz5~PaOcrQsNuN4k{Eo<1sf*U-})crcGY`PkEsJ^swDrwxL&3yD>lKl_0yQXN+x z$bwVnI4ys7AbEzv(c_bn!GwIhFv$@#X^uVZotT+~bD;=T-*@S~f z)f$B2FA$1PS{5qwsG6q7j+J2L$l|&5m~YIzDLv*J^JnRizwdIMYwS7Z2P}N~0;7f9 z5o+w_FEBrwq3%bgvLGi?9is{s;S%r-v&+d)4bmCB`!Ty3bR*Lp*;gu?j?qp~n!5WW zHW-B`lc4waOdPJAj3mn-IY1(QD7aj-v@_JCLS050>h}*;V<+?vTNS0$;iTqrY)NQI zuvhtqYiVVzP-FKGHcOp8m&xzH+o6^tTDkm2mD$vI>5= zio`+G1c_LXgH1wTlv4oVT6K;dRfFhpVF1#B6H=K;Xc!S%M$iGFr4ud42YpgSJ&ICP zzSPz(I2xug9S!&2i3TTw27_d3G{CY6vJH~HAQ5nWD~Z1obr>%54|dBQJ9c~wPpChN zKZTxl3=CZl{tv20Nj~&UkcHqICkZeXNwn}M%#FrqP#iVF!T?#4Mk^Ynm(0|wxPgv| z-Dc4;FfDwbNveJcMHJi<+yCB69(%PNzkZte`rg>4p6ijcSFA0QGV@3 z#&zvQR<$=mp*t0KHH^CoTQ6~VN?S>st7we%BEUFHlmf8ung09OH~OzJr|_l#Ga3yy z09Z`k1(W@vw?b({!NzIGlL1*qbcL(1wwUPgPYUND?;_NZL{V|D!@%>C> ze4&!TgHKNsl(}>XMl$*LjD(vP;@hd^{-_&BLs+GE_ zeVIbIhg~=y>DV~A0-nxd>d&gxnscfRpgpIah}wWK+=`zy%EuLVUOkqwK(*lSVR)s( zxd^q=ejN+8TUT0ib2+bRi7HB!!0tso{M4Q&ZY%-|^@Z6t11a9mxdBMQv<{1LOB#8E zkC=?iP=?kH=iDg|>l+6a;jj%$Own|`Wg$Ta@1~Jhmb$B;_zGL76}>!Oq2iqZ5A4oF z+tsTO47=4t5#DyODL8voRxA8tu}QsUf?b&ylkid=B25 zBFmTiJ%>WWTF|p@yPLeZN zho%S6iY=4e4S&1BuAF@#^JDnqXE^4H}W2W~CA^AoZZY!8tX pRRK6YXVaK@*1%ssWQ|IOqi2Wspv@!~^Cg>4%GG*Bd2xXc{sXt>5u5-3 delta 4547 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 diff --git a/shortdrama/config/shot_duration.go b/shortdrama/agent/shot_duration.go similarity index 99% rename from shortdrama/config/shot_duration.go rename to shortdrama/agent/shot_duration.go index c6123f2..2330da4 100644 --- a/shortdrama/config/shot_duration.go +++ b/shortdrama/agent/shot_duration.go @@ -1,4 +1,4 @@ -package config +package agent import ( "context" diff --git a/shortdrama/dao/background_music_dao.go b/shortdrama/dao/background_music_dao.go index d7a93fe..b3d412a 100644 --- a/shortdrama/dao/background_music_dao.go +++ b/shortdrama/dao/background_music_dao.go @@ -48,11 +48,34 @@ func (d *backgroundMusicDao) Delete(ctx context.Context, id int64) error { } func (d *backgroundMusicDao) DeleteByDrama(ctx context.Context, dramaId int64) error { - return common.DeleteByDrama(ctx, public.TableNameBackgroundMusic, dramaId) + _, err := g.DB().Model(public.TableNameBackgroundMusic).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() + return err } func (d *backgroundMusicDao) ListPageByDrama(ctx context.Context, dramaId int64, page, pageSize int) (res []*entity.BackgroundMusic, total int, err error) { - return common.ListPageByDrama[entity.BackgroundMusic](ctx, public.TableNameBackgroundMusic, dramaId, page, pageSize) + m := g.DB().Model(public.TableNameBackgroundMusic).Ctx(ctx).Where("drama_id", dramaId) + if pageSize == -1 { + r, err := m.OrderAsc("id").All() + if err != nil { + return nil, 0, err + } + res = make([]*entity.BackgroundMusic, 0) + err = r.Structs(&res) + return res, len(res), err + } + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + r, total, err := m.OrderAsc("id").Limit(pageSize).Offset((page - 1) * pageSize).AllAndCount(false) + if err != nil { + return + } + res = make([]*entity.BackgroundMusic, 0) + err = r.Structs(&res) + return } func (d *backgroundMusicDao) ListByDrama(ctx context.Context, dramaId int64) (res []*entity.BackgroundMusic, err error) { r, err := g.DB().Model(public.TableNameBackgroundMusic).Ctx(ctx).Where("drama_id", dramaId).OrderAsc("id").Limit(200).All() diff --git a/shortdrama/dao/character_dao.go b/shortdrama/dao/character_dao.go index d88d83a..fca16c5 100644 --- a/shortdrama/dao/character_dao.go +++ b/shortdrama/dao/character_dao.go @@ -56,9 +56,32 @@ func (d *characterDao) Delete(ctx context.Context, id int64) error { } func (d *characterDao) DeleteByDrama(ctx context.Context, dramaId int64) error { - return common.DeleteByDrama(ctx, public.TableNameCharacter, dramaId) + _, err := g.DB().Model(public.TableNameCharacter).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() + return err } func (d *characterDao) ListPageByDrama(ctx context.Context, dramaId int64, page, pageSize int) (res []*entity.Character, total int, err error) { - return common.ListPageByDrama[entity.Character](ctx, public.TableNameCharacter, dramaId, page, pageSize) + m := g.DB().Model(public.TableNameCharacter).Ctx(ctx).Where("drama_id", dramaId) + if pageSize == -1 { + r, err := m.OrderAsc("id").All() + if err != nil { + return nil, 0, err + } + res = make([]*entity.Character, 0) + err = r.Structs(&res) + return res, len(res), err + } + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + r, total, err := m.OrderAsc("id").Limit(pageSize).Offset((page - 1) * pageSize).AllAndCount(false) + if err != nil { + return + } + res = make([]*entity.Character, 0) + err = r.Structs(&res) + return } diff --git a/shortdrama/dao/episode_dao.go b/shortdrama/dao/episode_dao.go index 295c1f1..4d12d14 100644 --- a/shortdrama/dao/episode_dao.go +++ b/shortdrama/dao/episode_dao.go @@ -66,7 +66,8 @@ func (d *episodeDao) Delete(ctx context.Context, id int64) error { } func (d *episodeDao) DeleteByDrama(ctx context.Context, dramaId int64) error { - return common.DeleteByDrama(ctx, public.TableNameEpisode, dramaId) + _, err := g.DB().Model(public.TableNameEpisode).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() + return err } func (d *episodeDao) UpdateStatus(ctx context.Context, id int64, status string, videoUrl string) error { diff --git a/shortdrama/dao/prop_dao.go b/shortdrama/dao/prop_dao.go index 8a5f516..75ebaf3 100644 --- a/shortdrama/dao/prop_dao.go +++ b/shortdrama/dao/prop_dao.go @@ -49,11 +49,34 @@ func (d *propDao) Delete(ctx context.Context, id int64) error { } func (d *propDao) DeleteByDrama(ctx context.Context, dramaId int64) error { - return common.DeleteByDrama(ctx, public.TableNameProp, dramaId) + _, err := g.DB().Model(public.TableNameProp).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() + return err } func (d *propDao) ListPageByDrama(ctx context.Context, dramaId int64, page, pageSize int) (res []*entity.Prop, total int, err error) { - return common.ListPageByDrama[entity.Prop](ctx, public.TableNameProp, dramaId, page, pageSize) + m := g.DB().Model(public.TableNameProp).Ctx(ctx).Where("drama_id", dramaId) + if pageSize == -1 { + r, err := m.OrderAsc("id").All() + if err != nil { + return nil, 0, err + } + res = make([]*entity.Prop, 0) + err = r.Structs(&res) + return res, len(res), err + } + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + r, total, err := m.OrderAsc("id").Limit(pageSize).Offset((page - 1) * pageSize).AllAndCount(false) + if err != nil { + return + } + res = make([]*entity.Prop, 0) + err = r.Structs(&res) + return } func (d *propDao) ListByDrama(ctx context.Context, dramaId int64) (res []*entity.Prop, err error) { r, err := g.DB().Model(public.TableNameProp).Ctx(ctx).Where("drama_id", dramaId).OrderAsc("id").Limit(200).All() diff --git a/shortdrama/dao/scene_dao.go b/shortdrama/dao/scene_dao.go index d5ca506..4298369 100644 --- a/shortdrama/dao/scene_dao.go +++ b/shortdrama/dao/scene_dao.go @@ -49,11 +49,34 @@ func (d *sceneDao) Delete(ctx context.Context, id int64) error { } func (d *sceneDao) DeleteByDrama(ctx context.Context, dramaId int64) error { - return common.DeleteByDrama(ctx, public.TableNameScene, dramaId) + _, err := g.DB().Model(public.TableNameScene).Ctx(ctx).Unscoped().Where("drama_id", dramaId).Delete() + return err } func (d *sceneDao) ListPageByDrama(ctx context.Context, dramaId int64, page, pageSize int) (res []*entity.Scene, total int, err error) { - return common.ListPageByDrama[entity.Scene](ctx, public.TableNameScene, dramaId, page, pageSize) + m := g.DB().Model(public.TableNameScene).Ctx(ctx).Where("drama_id", dramaId) + if pageSize == -1 { + r, err := m.OrderAsc("id").All() + if err != nil { + return nil, 0, err + } + res = make([]*entity.Scene, 0) + err = r.Structs(&res) + return res, len(res), err + } + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + r, total, err := m.OrderAsc("id").Limit(pageSize).Offset((page - 1) * pageSize).AllAndCount(false) + if err != nil { + return + } + res = make([]*entity.Scene, 0) + err = r.Structs(&res) + return } func (d *sceneDao) ListByDrama(ctx context.Context, dramaId int64) (res []*entity.Scene, err error) { r, err := g.DB().Model(public.TableNameScene).Ctx(ctx).Where("drama_id", dramaId).OrderAsc("id").Limit(200).All() diff --git a/shortdrama/service/episode_service.go b/shortdrama/service/episode_service.go index 09b12bd..e21e3a5 100644 --- a/shortdrama/service/episode_service.go +++ b/shortdrama/service/episode_service.go @@ -6,13 +6,11 @@ import ( "fmt" "os" "path/filepath" - "sort" "strings" "time" "video-factory/shortdrama/agent" - "video-factory/shortdrama/config" consts "video-factory/shortdrama/consts" "video-factory/shortdrama/consts/public" "video-factory/shortdrama/dao" @@ -355,8 +353,8 @@ func (s *episodeService) buildScriptGenUserInput(ctx context.Context, d *entity. b.WriteString("【输出格式】\nJSON数组,每个元素是一个镜头对象(shot),包含以下字段:\n- index: 镜头序号(从1开始)\n- startTime: 开始时间(格式MM:SS)\n- endTime: 结束时间(格式MM:SS)\n- event: 画面描述——观众在屏幕上直接看到的一切。场景环境、角色动作、表情变化、物体位置等。**这是视频模型生成画面的唯一依据,所有画面内容必须写在这里**,严禁把画面描述放入 narration 字段。不包含旁白和台词。\n- dialogue: 主台词——角色在画面中亲口说出的对白。多人对话用「角色名:台词」格式。旁白和内心独白不属于这里。如果本镜头无人说话,留空即可。\n- narration: 旁白配音——需要配音演员念出来的解说文字。必须是**通过画面无法直接传达**的信息(如角色内心独白、故事背景交代、时间跳跃说明等)。**如果一段文字描述的是观众能直接看到的画面内容,它就不属于旁白,必须放进 event 字段。** 本镜头无旁白时留空即可。\n- cameraMovement: 运镜方式,从以下标准类型中选择一种:固定镜头、推、拉、摇、移、跟、升、降、旋转、晃动、航拍\n- shotSize: 景别,从以下标准类型中选择一种:远景、全景、中景、近景、特写\n- characters: 出演人物数组,填写演员名称,如[\"张三\", \"李四\"],从「可用演员」中选择\n- scene: 场景名称,从「可用场景」中选择\n- props: 道具名称数组,如[\"剑\", \"酒杯\"],从「可用道具」中选择\n\n直接输出JSON数组,不要markdown代码块标记,不要其他任何内容。\n\n") b.WriteString("【创作规范】\n\n1. 剧情完整性【核心规则】—— 剧情描述是唯一创作依据,严格按每一句话、每一个细节逐拍还原。\n - 剧情描述中出现的每一句角色说的话,必须一字不差地写入对应镜头的dialogue字段。严禁把台词\"翻译\"成事件描述。\n - 剧情描述中的每一个具体细节(人物的动作、表情反应、对话、环境互动等)都必须忠实地保留在对应镜头的event、narration或dialogue字段中,不能省略、概括或改写为泛化描述。\n - 所有镜头时长之和应等于 %d 秒(50秒至少20个镜头,20-25个镜头为最佳密度)。\n\n2. 场景一致性 —— 每个镜头的 scene 字段值必须与 event 中画面描述的地点一致。\n - 如果 event 描述角色在\"实验室内\",scene 必须是该实验室的名称,不能写成其他地点。\n - 如果角色从一个场景移动到另一个场景,必须先切换 scene(如从「街道」切到「室内」),再描述新地点中的动作。\n\n3. 景别与动作匹配规则 —— 按镜头内容选择正确的 shotSize:\n - shotSize=特写:面部表情、微表情、眼神变化、细节反应(手部小动作、物品细节)。不能用于动作展示。\n - shotSize=近景:对话、上半身互动、情绪交流。能看清面部表情和手势。\n - shotSize=中景:日常动作、手持物体、多人交互。膝盖以上,既有动作又有空间感。\n - shotSize=全景:连贯动作、奔跑、打斗、空间关系。展示全身动作和环境关系。\n - shotSize=远景:环境交代、空镜过渡、场景建立。没有人或人很小,用于转场。\n\n4. 运镜搭配规则 —— 按场景类型选择 cameraMovement:\n - 对话/情绪交流 → 固定镜头或缓推\n - 动作/打斗 → 跟、摇、移\n - 人物进入/离开画面 → 跟、移\n - 情绪揭示 → 推\n - 环境展示 → 摇、升降、航拍\n - 紧张/混乱 → 晃动\n - 固定镜头是默认选项,无特殊需要不使用复杂运镜。\n\n5. 口型适配规则 —— 有台词的镜头(dialogue 不为空)必须遵守:\n - shotSize 必须为中景或近景(不能是远景、全景或特写)。\n - 说话角色必须面向或侧向镜头,不能背对镜头说话。\n - 不说话只做反应的角色可用其他景别。\n") - b.WriteString(config.GetShotDurationPrompt(ctx, d.Type)) - b.WriteString(config.GetStylePrompt(ctx, d.Type)) + b.WriteString(agent.GetShotDurationPrompt(ctx, d.Type)) + b.WriteString(agent.GetStylePrompt(ctx, d.Type)) b.WriteString("\n\n6. JSON格式要求:输出必须是合法的 JSON 数组,字符串值中若需使用引号请用「」代替 ASCII 双引号,确保 json.Unmarshal 能正确解析。\n7. 风控规避:视频模型对某些画面有安全审查机制,生成镜头描述时必须规避。政治敏感内容(真实政治人物/标志/当代军警标识)零容忍。暴力血腥画面(刀刺入身体、血溅、断肢)改为反应镜头或切镜回避,不展示伤害过程。当代背景中的军警抓捕动作将「警察/武警」改为「保安/黑衣人」,动作从「按倒铐住」改为「围住请离」。武器描写聚焦持武器的人物(「他握紧长刀,目光凌厉」),不写武器接触身体的画面。暴力结果用间接描写:写周围人反应而非伤口血迹。\n") return b.String()