From b08067452440ddf1bbe575cf65d58ae072243988 Mon Sep 17 00:00:00 2001 From: ddcat666 <26564566+ddcat666@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:38:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(media):=20=E9=9F=B3=E9=A2=91=E7=94=9F?= =?UTF-8?q?=E6=88=90=20-=20=E6=94=AF=E6=8C=81=20IndexTTS2=20=E6=83=85?= =?UTF-8?q?=E6=84=9F=E6=8E=A7=E5=88=B6=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../protocol/autodl_indextts2_test.go | 95 ++++++++++++++++++ plugin-packages/autodl-comfyui.yingce-plugin | Bin 11910 -> 15218 bytes web/src/components/audio-settings-panel.tsx | 37 ++++++- .../canvas/canvas-audio-settings-popover.tsx | 2 +- .../canvas/canvas-config-node-panel.tsx | 23 ++++- .../canvas/canvas-node-prompt-config.ts | 13 ++- .../canvas/canvas-generation-submission.ts | 20 ++++ .../lib/canvas/canvas-project-generation.ts | 10 ++ web/src/lib/model-selection.ts | 2 +- .../canvas/use-canvas-generation-executor.ts | 10 ++ web/src/services/api/generation-task.ts | 24 ++++- web/src/stores/use-config-store.ts | 30 ++++++ web/src/types/canvas.ts | 10 ++ 13 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 backend/internal/protocol/autodl_indextts2_test.go diff --git a/backend/internal/protocol/autodl_indextts2_test.go b/backend/internal/protocol/autodl_indextts2_test.go new file mode 100644 index 000000000..8a92092c6 --- /dev/null +++ b/backend/internal/protocol/autodl_indextts2_test.go @@ -0,0 +1,95 @@ +package protocol + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" +) + +// IndexTTS2 的音色完全来自 prompt_simple 参考音频,缺失时上游返回“缺少必填参数”, +// 因此插件必须把画布连线的参考音频映射过去,并在本地拦截未连接音频的请求。 +func TestAutoDLIndexTTS2Workflow(t *testing.T) { + data, err := os.ReadFile(filepath.Join("..", "..", "..", "plugin-packages", "autodl-comfyui.yingce-plugin")) + if err != nil { + t.Fatal(err) + } + pkg, err := ParsePluginPackage(data) + if err != nil { + t.Fatal(err) + } + adapters, err := LoadInstalledProviders(pkg.ManifestRaw, nil) + if err != nil { + t.Fatal(err) + } + var adapter Adapter + for _, a := range adapters { + if a.Metadata().ID == "autodl-comfyui-audio" { + adapter = a + } + } + if adapter == nil { + t.Fatal("autodl-comfyui-audio provider adapter not found") + } + if !adapter.Metadata().RequiresPublicMediaURLs { + t.Fatal("IndexTTS2 reference audio must be sent as public URLs") + } + + if _, err := adapter.BuildCreate(context.Background(), RequestContext{BaseURL: "https://autodl.art", Request: GenerationRequest{ + Capability: CapabilityAudio, Model: "indextts2-v1", Prompt: "你好,这是一段测试文本", + }}); err == nil || !strings.Contains(err.Error(), "音色参考音频") { + t.Fatalf("missing prompt_simple must be rejected locally, err = %v", err) + } + + create, err := adapter.BuildCreate(context.Background(), RequestContext{BaseURL: "https://autodl.art", Request: GenerationRequest{ + Capability: CapabilityAudio, Model: "indextts2-v1", Prompt: "你好,这是一段测试文本", + Audios: []MediaReference{{URL: "https://cdn.example/voice.mp3", Order: 0}}, + Extra: map[string]any{"audioVoice": "alloy"}, + }}) + if err != nil { + t.Fatal(err) + } + if create.Method != "POST" || create.Path != "/api/v1/comfyui/comfyui_workflow/indextts2-v1" { + t.Fatalf("create spec = %#v", create) + } + body := create.Body.(map[string]any) + if body["prompt_text"] != "你好,这是一段测试文本" || body["prompt_simple"] != "https://cdn.example/voice.mp3" || body["emo_control_method"] != "与音色参考音频相同" || body["emo_random"] != false { + t.Fatalf("single reference body = %#v", body) + } + for _, name := range []string{"emo_happy", "emo_angry", "emo_sad", "emo_afraid", "emo_disgusted", "emo_melancholic", "emo_surprised", "emo_calm"} { + if value, ok := body[name]; !ok || manifestFloat(value) != 0 { + t.Fatalf("%s = %#v, body = %#v", name, value, body) + } + } + for _, name := range []string{"emo_ref_audio", "voice", "prompt"} { + if _, exists := body[name]; exists { + t.Fatalf("single reference body must not send %s: %#v", name, body) + } + } + + withEmotion, err := adapter.BuildCreate(context.Background(), RequestContext{BaseURL: "https://autodl.art", Request: GenerationRequest{ + Capability: CapabilityAudio, Model: "indextts2-v1", Prompt: "测试", + Audios: []MediaReference{{URL: "https://cdn.example/emotion.mp3", Order: 2}, {URL: "https://cdn.example/voice.mp3", Order: 1}}, + ProviderOptions: map[string]map[string]any{"autodl-comfyui-audio": {"emo_happy": 0.5, "emo_calm": 0.3}}, + }}) + if err != nil { + t.Fatal(err) + } + emotionBody := withEmotion.Body.(map[string]any) + if emotionBody["prompt_simple"] != "https://cdn.example/voice.mp3" || emotionBody["emo_ref_audio"] != "https://cdn.example/emotion.mp3" || emotionBody["emo_control_method"] != "使用情感参考音频" || manifestFloat(emotionBody["emo_happy"]) != 0.5 || manifestFloat(emotionBody["emo_calm"]) != 0.3 { + t.Fatalf("emotion reference body = %#v", emotionBody) + } + + if _, err := adapter.BuildCreate(context.Background(), RequestContext{BaseURL: "https://autodl.art", Request: GenerationRequest{ + Capability: CapabilityAudio, Model: "indextts2-v1", Prompt: "测试", + Audios: []MediaReference{{URL: "https://cdn.example/a.mp3"}, {URL: "https://cdn.example/b.mp3", Order: 1}, {URL: "https://cdn.example/c.mp3", Order: 2}}, + }}); err == nil { + t.Fatal("more than two reference audios must be rejected") + } + + completed, err := adapter.ParsePoll(context.Background(), PollContext{TaskID: "671ce5ca"}, []byte(`{"msg":"","code":"Success","data":{"status":"completed","results":[{"url":"https://cos.example/ComfyUI_00010_.wav","type":"audio","node_id":"9","file_type":"wav","output_type":"output"}],"task_id":"671ce5ca"}}`)) + if err != nil || completed.Status != StatusSucceeded || completed.Result == nil || len(completed.Result.Audios) != 1 || completed.Result.Audios[0].URL != "https://cos.example/ComfyUI_00010_.wav" { + t.Fatalf("completed = %#v, err = %v", completed, err) + } +} diff --git a/plugin-packages/autodl-comfyui.yingce-plugin b/plugin-packages/autodl-comfyui.yingce-plugin index 8af8ce60d310659fa6b7108b5e92f2d9442bf710..a163beb682287c49ac33f15d848aa6d0f0c144eb 100644 GIT binary patch literal 15218 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 literal 11910 zcmZ{~V~{4%)~#E%`Ic?lw%uK}ZChQoZQHilW!tWLyWEA-`^JrX?ziKd%!tg$nQQ#X z@gv84)>M`Qhrj><0f7M-uVK|&ZEa@izy$$$g9imc{8zOzvbQofb8%&~c5$%35wKCi zlWN<0!|c8Gm)1SFfp$MBtE)eneZSq5p?Lja7c7S>2RM+#PURS==;8i zoE*KDO8Cl*gavT0rPlaZkZ|6yyz`C~j9OF?BR4&1S;LSNgRH9vUqU`O>ZR_^%0RXE z3C$fgHJ1;%QrftQyq6ZeX7l1-`+3h0)LX)_w6nXjE7Rv}{l9OF<=O=V}Q+*-ic&+50E2*#D(j_mQ#FkHS?J3vUZqs42-Ireg zPd!#z_fp5nR%Y{4hmcPWyU2w0k0YFq7TnE47rELsG6BDec_@THhnxgy9dC?T$9{p% z)DLfK-VD;yGul}+-7jF#GY($#IQz7wb%Ik#Rb|#6Y_BTPAdkbz-;@AOMwy9yHL`03 zCurlPXg%tJ8zzuLL%F#oe$9n>$hu6$N;#fT=5*=PnC%}ct%RLS9eQ`Z#;Vd+wWgNN z0?m!eQ<$*fD_SGZrdn0RaVe|1+Eo2H8*_0A#Q<^euq1PSsJNG~evxCWw)pa7;vVbP zrm@NbMlVLYG$+H~G8vngP$!E7C#x^_3mWXS^5cBNTbZcrb&eeM+n&KAE-2Pgj2G-}|1j zU|jS0gfsR{NQ@4ka<)i;h~>bkDZkp-*|XG4Gc}4@iNy4zMpuI5RkF{Q%hUP$1#;ZY z$}7n%C#F)RgvzgYP?415-+>dOZ}1g_ZNo4GYJq*|I&~^qT@p8fLtxgKuqT_}Bq6>o zGM#S%Z_?t@nFCOSI_@B6x2vo;HnPS!93-0_Gdzc~&uK^@I#umolaNC{XMw>&tjOz3 zy%{U>^*l;*UKcuySS4S{}WS!Ii}rWBpl$~8(l~# zx6)}OOcvZlQUIp^O@;5Ly_*r%Uq7@M0Zms>c`|x5E|xoii44wObs69m=QX0sM2Ubj zY2a!AN0C&Rg7To3-xBRLekt}#pPZ&iN2h_katt%>@ft@|)KICSS_cl#^eKKOL?01H zw+H;JE#jKH`En7}wpmn9$~_u5?Mc1k2QXpty}5Ri{Cv=gAt#pWDlx%a*f;e>@B%u(ZW1DFs8Af zjHeUdReaOnXv&BY%IMZDTSW0QLYHxuMAzsB+a{i#4BKSj5W^Wq6#ku!{ExwYz;LY! zcdDAR+SGLi>v!|!&D6Y5dUWRI5L*aI{aSjcCtRs=O(&(jLT%+?u*;R>Q=GFO>}eit z^Sq%I8p+|&q%wJC!l)@d#@R`sDaMl6ct}b3N~FR1bK+C})GxQTuWm5ie3AoS?x{&# zzF61c{rE2xr?(lspA1gegs_PU|dQ@%zaX*zwCu{=vlFEi%B55 zv2P365Cy3({g~7+FF*N6Sww^-U8k1p!Ctc;~Bl=NrbHQ1JKMz-V=9B}+=-Au-NL@!qyr=IO9a!@W(X zXKhy}Y`%a7a0A$^l969(wX_vrZ3F(z?{eF#RxW0tl*jjHZHee9OtCkh?ru1_{9Tmj zDfgoRxuvx?M9Vi#4hFo39Go*bq}pR@o)V=BIQHrMfcttEzbgbiHF`x}9{i@l%?bDO zZq+_M*fGc0@;g0rs88EGDuT6K zXCXP%B@@G};Pvm9G9(Wx=v}j|9g@8_7zt+f&+4XovMBYatJZ55Zf#X%nzusD%Z+I`g%T;(3JUQ^Afz%ffR^Ufw#6@N}p_`P&4r<=F@N%5d2U@rqT)_%&)7XJoPQEwn;A{^cnq)h6w&NcP;rpU82eRgJHiM zuigFTKp7cnrs3~zTY+Q7W~2sMdKL1qg}wxM*zC>aQD*ivO-`vOffPl%Q<%Ubu=hR8 zSfeItUHR#r5|f`%a{wO485NrwoZ_GQ;GUs!PS{}?#PB_RW~wp8xV2(WPW9MwiKbIc zgxIn0k?t2w;y4>VY>%d1nio6@kN90Hh`Rm8Xq&bJs)j=>qxJp@)t zLn~!(8h6V;RO(ZV79z-VPQ{A2$KXmGa*F+hsvS;d$0A1Jl>j-^r46g*Z8KdI2_ z(9X)Ep`}}2hv|B0dfjV+eKpu^LUnPS4jEI2j?`h>Rk-b|-TmNOo>EP(kc?3_Xm818 z8XKEdM-(?PBe3y~6j#iVc)99)!V70OkpYU#Iv)M*AZz zplI|Mn~_pT3>;kg2K(+HAvQ9 z|LsQG&8my{LGI{-BH7Dl(179dX++!{O!HXfy}qQtn}d+PQ<9!Ppxd6P9kL@w4@0=nU1dU}~u@EhakY z`m^i4^q%ICF{}56I3w+=Tn;w0SF%zAz)V)Cb%|1)R@7*qEoG4q6G5baA}^FfBku76 zC4#>!)RQzt+SvV4O7pus-I+`@orl0@guCv~u695*|44V!_;UCA#5%kA)5c*jrb>+o z{8;bCm>ym>HVg3mZ9bjl>%$^wmuvpDxv=1Ko$4sqmp3n|))xUBOU_?$5zBzURA$`_ zJ2rpDug%q4vFS#k2{kUXSjgqx9PyRaN9j3I3VWQsKXZs_OP14yz%p}~56!%D&Ojo2 z70-IZhoTAN9J#M~4Vpj8=Mj4MXp2hAzYUA@Cs&x6z*NA4;4bNY(Jin%+110oE| z#;CKWq?C%RjLKL2^j&expcteLMGaN*oEt-Jvi3Q1-TJqz{Ngb+B+UCC5`Fe^ir9?e z_k22o$D9RD*)FHi+N<;VRNliv*Um=x_{&y#Jk6ffB?p%W&FBq;qsDN)zqaffYKV{k!Oz(!gj37mRjeURoy= zoCwSJca|(N*|0PmC@}c&$&KasZw`AdZ7ru+u$`0H*iaj6DU_Y2Np@4W??ueGNUj!O z_cfi_U_)=DT{p)0YRM@2T{$fM>jOoeo7&c0?$@xMh+K<2 zN1uO7vtqey9DnRy=-oKg?Xic{XYrs*_~hP1pnB1y%Kv7(!Xr=p*GiH6xxni2OXWGN zCPH`D;nLcb8u!s-MkTfo@1drY@ZnE1P4SQvo`3I35G|~EUX%-&Lqu8a-Y*|d={0=s zPAFhCGF!Y-E>{!t&B5-}{M1BY+kVR5?C{}UmAsV!`ghpbmAc>k4EVKW3BNhM@3=L{ zvmMUF;ps)SeVxUYUo{kL-C=&v%9mGUk`)I3?2j^ZE}^k~tP00>ZU0tglXsD2;0-hH z3uzG>YTkz((F18#=T$V{QTgDvYu{wp_cIjb#IZP{mJy0Y9YyShH=I?9L(qg8SiCDA zV6$WI)4?@hwwKG;^R9!eT>n4{&BRcTT(PI^I5xckVa@Hn!I-x#bhhzwlj-{Vsb5b@ z*vzWy0=dK_-6(aqV8}#3YC+2d9$R86Y(W-PVk!5UMi3; z*UH1~Bq$FW_UWqdjPuEo)LyL_cJWc55mmpf%@YwRxMClq95$G$ z`nzKLt7N#jx6TtS7?U)6$mV#?-#*)5^>}ws@(rW4ePcPdN6q`bFGfc;d9yr)Xtutl zZ)i2+P*?1d;D__<0LJ?^R>a)iAS;+>@qYKg4V&K3=)L1-ejJlGVGk4`{6Lw5kmmfW z*x}yXU*7SA{!5djJqtdM_l5ro&v0?f1bmx571d`Ah_u$iyTT~H;s|b9b4Ms9e#eXH zjd$}A>@#;A?d1OY-EX^(L`1nGE`;Pp#Itz;^6Q=?LG0W>!Kh9%?{?F%^O}7@$iGKz zDv^T$h~J*172MegJoPmkL>g=QVK@k|zi7Q#SK)$pzqT|S{Lo^aS<=&KAVkKR`H zSF=X`n64T=wX)IK3GzQ&!24uQ<0*|Q15a#T`zXAfjVuD(6XyP#sfD;-^o=BXXtZTU z4wvha?U@q+6=fyd%C~&38e7T8U|6)NU6IA^fR+D`0aZ2<5knH`WJ)L4e+8<0mNFI) z?Ikp(HmSv=oEFds$4I#x)7Nl&G|>cZRLXP-jlXXb7vF8Pd0=`sm8~g$X!^IHN?8rrT2|tEG-UU1|pjOY!?XfI9*z#SBF!5(;-e3pzH|I<_Y_~FN4k{&=wE_vfM4z?a7^2sCe+Y9Y zo#)XhGu937-KBG#rS3I}&uCe@%etHxgT`V0zu9WnMcn&$28ONN=&8`ZUyVa-Z zyJYok3UJC7s-AwnieC9Y;i}E~j5de5fZqHdvoRn|GD{cAg1zcEiB2543tG{EYk|VC zV&pHTBUE|e&bOp|^Vc(%0CU&-tS~-EZ9wFoLt=-9ky9hGV>A=15tVUrgmny5*-6Cd zHewjDp(qrpr1dG%_{}wg`Wd9GFD0rXD0Rkhq3eVq$8i_2*G(fSbHKY;ro)b8bh-mp ziX{>SVEHFY1?}G#PtB)sZ~gs!U_K=7-zMmE*vf0DAHDKR{=VO6a{h59?(;)aE#29K ztsRfQhaT49tDspVUfTI!Dtpx)&Qxs)qI!jzJN+k6=GIpnJ5EQ)poaBeyKUA2vI%Vx z5Q7B+`Fwi8id(sf8U()h`&WL-(Kxx|b-h66JYw$HUn$2iC$|>lHGz zM3$+>t<=azX`2f%fh0hC3p}naL@a-AXACrm_;{^lLNXFa1lX$^-r12}0T@|JDdwM5R~VM3W+qp|GFWm-$Ch`p6F(ZrDz z538DJA_#jv8)dN|kiAzS21B%TgZGUbu4h4E3A^8%Zz)mEE?3Si37Lwf5cBbyg%-2y@d#pK+#MX7&?FQgTP{l!Cc*? z_23P@>X4C%=DH@Nwg_sKYAUgo;N2$!vl7vBA_@5t3|{J8Bkz*PbPiBs2D-`1{-KCP^ z({fL60z}sqFXM!e{<%=1v8SYHoxVssOFBBxAgQPN+RJIHw#`bxZ1d{%O_S=jteReZ z8=ib~->y|C>2Ld5$b`2AZ9L-7=+^o%V`f{MZTIzhX&?VIPG-^?PYB85RNO=pDS}uS zKjD8Btbgw#9=Y*Q4VZ?ZR>>PEyXA<%Y@A;)f03`QLVOeTqi#Bvo_vEU|Bo%M+>BDN z7EllnH82nm*neAGsuCjN3KEQVre-Qi4vU6>{y#LkBhmT^oylC-y%_+9yqE)uVoUUH zN=)>pMY>EbtvS;vnZ(6WFB6_uNHLJ%=A`tM|hfC9c{{(rhY;RQg%Q)a6vlm zeIQ49Wk3harNHFifL#UJ@T&10H+(wPL8RF2K@m;nK~-1 zgbJ+yR@BSI6p`^f_zTGMW@3G?AAZ0)uAi3Us`yxwgDV(e%a6WMEjkUhfTL`0>zn#Y z&r_g*dBx@Wi{VF*vdlJW>0Cqgx2tE%N=2)V=iuEp{-rWyNJDzC6SA=fE?>^}N;ShNL8T zH`}@9KH>f&xx2L?{#cwOVcN-!#oB0YRe`$fmZ(0hP?z_GE5peb`2VzapKm5}5d{Q9 zJsboC^`Et-4kj*4R`#xD&gMoYX8$a{A+S^1l6>zD7|q*4wmTgdLlI@|opVB5o7Vfo zirATHLuEqu3}$UNO*P4`docDJAa4S$5Sa{uSQs|I0h&aqKwvu&K%+nTHDb`Qp{JE* zCX)tB^RJf-?D>1$JTLgS=QjES>pU)c`yze-TDQdEd=ZCOp|;?H==VE=)saQ|Rc$-S zlzciTr>qWg)SF#;HKg(-1kZxdPv@Z2F+DPn??Z66{Z1vH}T|X$cr+osV z$S$Cw=XC_PA}sQkCQ?YzGXIo=Q3U)M(=!+{x`{xD9U){LaPJ#(JYX05wC~GFiUpVPJ%@IxSLjBNZ46a!^OfB+m9!h{F8`k*6#+nxD@webH1*!57K0xds~Wa|QPwMDG{U|&1{ zwEerX0pP=-s=OM`{*4)O>K7~iYpQxPeheqhi#I;!3mo!)FF8`WmLi^Eu!D_6yC#|! z{FxCL{XS4(mKD*=WV~pYc6L*oWl}n|W@?KKenf4-FU}Y5&sfOLbrXlC|BZv?b+mXr*&_3GX0{v`8tD;%nP$Atr7N0{O(56fxkZA3*> zByjmaUBFjm{0Zd3-Z&eoCk|Ki0LrwXZ5EuaeqX*s=L&6X*cNqW(qb}*3Fyg4iR)*8 zPng%mYL)91Awx+pnSwbpVEh*`^6!rwXHp|+=?s>#Vr|9sXUnsJ$%I?m6m%C$@0yfX zxY;{(-tR|NGwXa(v|W%_4krVhMTyrBW6>$5WaEipOO!>~c$9u14nmZF>o!@0m{G+W zl32|-b_X|6C}^O0S=HlY7=6~4gc&+21SmWjg(^$1o2zQj!lI0vZ-%14bMKHZOMXek z@f*IZAZUP1{Z!`<(+HNIbgD;-*OZM)RYi#4?EPd)iWOb;6K*GTvVwG{&FATe>HwRH zR}GCGXJE`FvcuM#UP^S{s|)qNrXDSyV-4V!1J>O(EGDejly zih{46k!a;jc8Hy=BxXepijQ0WZuyD|Qx)`6-g;;_6`t_0_lr@CB`JiAN5%r%2?Atd zz9V0c`jCRo;-)zAScm_4(!Y5RePNOPapl9t{pTJ^E- zesQ7gWvrbS5JlPc43})4;R_{BwAMh@83(&uP=-KzidGs~u4;@~dK$e2k44au?N*?1 zW`O-&Zy3LuN~W6UcTEI93vnEuz!R&59ffNVL)bTLXD@`v@Ou zFcysO(=Q52fuo9u0bo;W5Xw!=JM^I7fk!cG<4}Kw%99Pg(piNIEm_>15(R2B zG%neTN#ol}*DD-t&(J=(f)g9y%&R_$!mvIKM@XN(IGY+Uk+&7!~QT=b|v-v0g?N5>VZ)1m-@!o>c6Tg{_ zX8DwxkYJeKlGXR{?r&04%dUeMBr5W8wU;YmtJjyc)3pFsM$(H<)oVRIraeHG7g?Je* zBBKl+xf7zLj!;vn?#P8(e*mTv;-ou_?GNv?e=5~475jZ>QpR&h+yu&~S7m~KHAqoi zX2Kv96<8;lQQ$U53Uz^0eCbxUO=Y)^#WCXFr&!j%#hi9Aq4Asd7hnHW|t1p__EoQsSRoJi6QVuUKIqh$e zDa8aHcnGkJusS9^kd92iU!@6ndTh6Ru-kv;5{1?_I`-tpdPDi$3K;`9L$9pWWVO-F zNKw`q4dCKM>|R(w1tXOw5Gfa%cGk$NO|C<= zLiK%dJuOCGPfGMF z3pba(uoT+zLy_W4YPx!H6RpdqH^U%}Y6&=&#LpJjY;KHv+-ajBBNsdUX47KAKPd}M zcKmII4MK+LAHnZK!6;fd6`Ro{XflMN#=wT-Tiaws5+WbdITWsy7`E9bO%O>>Yf86N zt_~~0_+u{+R$|?IxOe$0*##0l*fYgCL#|hsF{4IVMI+A6}ws z1mPJ;RZM^~n&CKgR2XAr=DA=v6bAo5XT<4=6@5w}NWVNU)W&wHehy&=!b&o+wkRdO zrYsn=88|25rcuF*MG6V$7&CYgqse#I>S9og!j4;Ypr73jHS$Ati46$+VZ~OQ=)MD^Af+1VIXhNFkI*?g$uVpw-Qaqq^>!NFD=MqsD{}N< z4qL~F#WC)WIC@I`$bRP=P1&Yw-P6)csbs`uusyebe)R=7KL?H-LvQSioJYb0O zV;x#s-AI*Y2_{;SgTI`>Uv2IiE}50#Six1r{xIaaPY|x%jbjpl>dv+6{+dMZ79Vq-1BH!gq<*cD zF*ags#3_vs3N6*rI`FgOcS0OD$hLzN@gA~UT8IB8b?36?rIR{yqaUS4$|8ymIs%SS z1TLzvL{b|IveWm-<55invpk^x~~pWCIMHIE1~M<=+hFKjZ|ppSBUD}qTu5uj1`MxuXgvA^?U5qQ zolD|$$^O}QOXl!hM#~Tl?bD3R`YYC8ZM{764(is_6sz{&(+)8D0!r>twU4dwXQWt9 z05u{!EQZuwLeePtea{Cbu`?&r9h6Ps>=gB>rp!bcm8%&lBp+cB1u}Yb0LTkAc)}X= zjRIccR5>a-yRC8>VkdKpxkyORp%PhD#C(Me41-n?dnI4CVYSN510mAR*DX||UWb!jmIeI_W zQ#^KvfS;b*>5sk7Jq=J*jSu!yY+N_R*%NY4M1O0x)1e)*WZbmPn^Rl=fh0!)h?v+y zcAhnRG?R@bj=y>GVot@vS1Th$u`^g?gvzE?YgHUJ-p@9k`cv2IXsUdh%PoVAOyPeq z#dR*9nzVvmFd>H0v!k&Te#lhoXed4wu#QF&){JPFC8G_t$wC{t50*=kxec(H|Ik99RBTDTHcUY z&g#v*o>|)TOv6|Jk^9+@(I50eqUY-uVapQ%v||Z03sEw%F^d~7>X8m=f}A#@%}X#c zq;uqhBetL{AAjvQo4Kx}Tz%icc{s>0=5Y|Oydwn^h)xuChsy2y=1X1BN6aI>s?sQo%;H|#xL3k z64hzkYsPWstpMlo*VNrumTQp!kC2J9+qge?-iG?qsovjaSLQ=1ER`$~cP7%hr7whqq&t6e0&cRnqhbF4@jQe<3ap;=dL6C^_kNW+Q z14E^aL$^`AEj__DUecAg4j;7y6?Y|t*tM4qhd=5*@~j-&etqPgHZoElVgH$6S%wgKSf6@;zYA^F}Mtmb4PZ(oK9Sw#SMq;6;ZT5{ZfW=&=`d zin-|4JFMscXiF6!fKPV~tZH;O(*S(CMaA}Fg*eVk%j7Rwg;>djpQ4qrkej|Ew7hwj z)E8K}=;LYx6iQ|epS!|q%WL&kDRJSXaf<{Lr9QbsVkfP{P%6ZJa?*Zbz%rC0td2vj zI9yELm-6V-HLhB}++ugdAMPW|a}AKQM+jT^vQaOpQ=6V!+i6R#Jtv94v|X-Eogey* zZYXeiUPNm)^(|$%)enuET+c+Hu`9iZry6vy1fO>lB_BIwRF+DgNr_Q>WrQ3XU z*G~(JpAxlWuloHFKVIN*=T5_#F+EH0|KN459$okF@!d(WA^Cr<#OB;$OAM~DTJ~TD zK!HC#>p$Y)m$k~?Tq0krQhK_rNIlV4bXRKGObx8`p43wYy<d9AEwZ{#ZhluFXxB z=uhY0(;BV|=rqrb^X|qca8vx|;q~^61bcH5Wy7lVIcZ*uevXuae$LX zD8BcM-*b@_#3Auge>l!~^eovxg|ZyHo4jt3)E-%_w*_n&^^w{A{UuL8R)mQ>9)Vwl z%Clf)C9deoD>*U|Vc2%F7tx}FmssYXx86t@$KlmUzSl@e@C!c)po0#~|B$S(f%t}T z2q99s event.stopPropagation()} /> + + + +
+ {emotionFields.map(([key, label]) => ( + + ))} +
+
); } +const emotionFields: Array<[Extract, string]> = [ + ["audioEmotionHappy", "快乐"], ["audioEmotionAngry", "愤怒"], ["audioEmotionSad", "悲伤"], ["audioEmotionAfraid", "害怕"], + ["audioEmotionDisgusted", "厌恶"], ["audioEmotionMelancholic", "忧郁"], ["audioEmotionSurprised", "惊讶"], ["audioEmotionCalm", "平静"], +]; + +function normalizeEmotionWeight(value: string) { + const number = Number(value); + if (!Number.isFinite(number)) return "0"; + return String(Math.max(0, Math.min(1, number))); +} + function OptionPill({ selected, theme, onClick, children }: { selected: boolean; theme: CanvasTheme; onClick: () => void; children: ReactNode }) { return (