From 953879d19a090bf9a2d30f906dae8bcc5bcee390 Mon Sep 17 00:00:00 2001 From: Yamen Ahmad Date: Sun, 12 Apr 2026 03:31:45 +0400 Subject: [PATCH] fix: correct ELAI render URL and split long scripts into multiple slides Made-with: Cursor --- .../__pycache__/elai_service.cpython-312.pyc | Bin 8041 -> 9641 bytes .../encoach_ai/services/elai_service.py | 95 ++++++++++++------ 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/custom_addons/encoach_ai/services/__pycache__/elai_service.cpython-312.pyc b/custom_addons/encoach_ai/services/__pycache__/elai_service.cpython-312.pyc index 5caae276b254ca2bb034b95ef922029209a77984..41b46b2dbc612d2c6bb1f3e6270d6ef12433894a 100644 GIT binary patch delta 3057 zcmb7GU2GKB6~42xvwz&_U% zGQ*;X2fJ~FomhQiW8hYFf21A`dp8cCu;JM181uvja$dY1MmoJ=j4! zq*wNtbI(2ZoO91T_k8ZeGrYwmS$hAY&NkuzQa zLnh9IshPKMPR@LVS|S_p7^>kI&cfAj*6TE9yP_Mia(1j|yTHU;cTVVfEd6O+RkK1F zOlkc8WSRnC4oH|*hYXfBc$s=P1OTLEFiIT<9L4E!l%kV$Xf^C>!qTM;dkQ@YIK9Fs zdYL`|PLBYf7-U1%h`!c^4v5sWS7u=E76**xV2jM4$%;4%jv*AgKY-mg%6haO^6F)# zYB8Z{Qb0B+%=8It-6%5)fWuA~zztfUD)N*bXj}BK{skkYEk^X`DUrs;DyNJYi~l-4 zmdi+Nip>>-QC3XD%$USx1gXHL&yEZ4imY@tC9$HEy1Nac)hkSLn9*u=7-n!BT4k9}!!sb2qF|7`zC-S*o%A9{Nx&1xtz|IUnY(op)+TQ_+?^#ta6W_y0IM|IYI z;qYJYo}XCQ^GVY!ZYj1J?!KK`4ez=edl25QI{kCDS=;>R&HT0eLgA4!p@yQf#;>>U zo_~A((Bh94rNuL=q4-^H*0|;XzUZ10SUp$wPVJrURjux;N2ZR~cE!|yerDZUu4 zgKU^=c5~5B>X-D@WyLPrvvowQ$4>3&vcrp5x5=P5WJk6EQAQ8LYTQ7B7Q2=s#rc1Q z(I`7fC)pZElXhgqCA+GDSg98h=;oSbr{Cl}+k zUDq84O+aeYl$!x;P~4lBlx-pA>RV)|?AF4ez$w7BD(*|}>C@FO+UE>V+t9SDwHX_@ zrFggw#cKw#C!5d=Z+^2~#H6j2>kI+KD>#UI^m}Kx=6PjgyNQM;E_-C}rql0owy10? zVmRa4ix_OQ8{rbN2VZnj_9DR*4!k_5SJ0v>5clFc&};i-&wDz2KF=l31of>99Z)>q zC78o64@SaIr#fVh=1MC$3cN>BTX8u7&eNyB1Qq*U@t+8~lcm0KsgO_M=AFvrF0hrJ zC2mCwu?iXa@tl+y%kgXl88CJfwjhUPqw{SdNE~AWhKBeU592S#k~ev2910@3<(|^P zL#W%MQYVqs+w>ZSU4YwcN;sDii`@j1de}7Naflej&S6+8uoXmU;i|E;i!t^LEaX`% zAUzxPcn?PM*htZltf-4g+^8|WCE-3IZA71XJ?OS~ntF`(`i@f(H0!I4!4t%-nHR?M zJWNSEJWA%qkz8SPl!s+~MmSS|q*bGjeJ$wkzMk&WcmlT*ts0D>(zrO1E^vIA#-I}O zLDC@LF+z3|@-`tS(Gh<~7krcO5+-Fj!<{cP6+|f;$5Iz^g%nrTNnP-Dva07&QVJH) z2mW>wX-6c<=y(1=$9X&{)@}uqN`Cl}%5l?pRs1pTYnQ<@_XmMe=_{A-udb$5SJUF_ z4_zIT2G!-6H2f`ur?>k45=u-Rp4>m<+vxAAKd6T4)kupPY+amK4JOsPCbgmct4Ny~ z?YPyy8r`wZ&|&wQ1+Z;uW5=2Sw8YisnA+5$HpkW0Smke<+JYtVb&n+&TB8T8tbg6; z47=9@k+A(4h}ax!Gzcf3?g3`!RqK>>#drAg{VQ+0b${T|so|B=>4&GdMuNRD*Z9pm+TLMKh^j7e%J~A{K_so`K;62 z@25Z85$Jc(%T@+UmR$^%EcCc-P%y0hwyfYO-l%;6W3Jo1=_k zlq)2K06#?ksEbm^(0}VXP8!_b=zL7$6B-!YYlK@%fbBc(9$R^9=qVVc821xd=VmN0 zNH#i&aVxxuKV>7SbNs;IWim65^3q_X@ZohlhnG-3nxsBLH=?!Fuh4Izit(b3vU~$B JqMrKX{{YjL_P+oC delta 1733 zcmbVMO>7%Q7@gVmdi}HZIzRTtze${p6UPaTleP&48d8d(btNJy5RHP0qAw+}mSIUF`Xp5L}=g{bc<=PraNu!)feoXETYCwas#fJqCo5VRr=S&z0gBBdUTiz6wxah0!b;D2jA(GYWnlS~E4KD>ltm zY@%s11cbIwTCLbMd(mxdupd$Tf8;yX_ROp?d;1Fi%V4J_5S!r@6Ez8N*x{uvyVhuF z$Ww7>j)u!A2vkoaNzUyx*nUK^CKx8ji9oS^f6id2N9{EDqorjuEB>-ISQStd^(`|e zK^IVchFAtlXT|w|fuJ$7A5pg^Ximd91g8LsRGjCXmtJqgIXDO2W>0}RNOr!ztQfve z>%>g4DB}sYR25OI;<5rwEJhv{)iu5U%%KSC_koJ5BtBxErFKdt!@vUviT%qi1DdG0 z*4RUhsmCp*OKOd1BI#~ib6o{1ta0;5Blo%s02-u+!%w>-Q$(1k3r)tA(!_TeQabUx zI0qkU#nFoRc>Q%rZhPD~uqNU`NouNZZxkjvG=B!aCdKhz(h_qU;>&G1JdTUrF8j-5 zS-lJRetbC6rTekMBAx;Gcaby=}@7qER|1Bni}b zB{w^r$>wm@-!0bo)6+Bgd`_vE3Z*w^@S;C?Xo5-;RBA7)8TGC5>Ff;3)!4GCM4J?P z`)lTMRw>M?$|#i{qi`JW`eVKtKbtu>J(EE-MjcdARBA#QHKUa9M4;EyL=yNyK!Vfw z!$8oHCd0Cobm0Qn74XeKwYqHvZXW@EKo7}!M^q2?t&VI3hxN`Ly}SSSj+h>f->__j zpWNZtcF(pA$T2+~$GiR!%xy>FoYdqR(rOk$_S77T5(g6!MjUG1J7DbUos z&4wiB_F>>@UrO&h2g5D4MgDFI0_+IQ^KGlWtG*i}>z6_oVfCgs9#18~w`1d;lmmR{ z9wCDbhcz|AZg`~B5W6wJ(aaD>Gb1eJ$za;XZc5CtB)jSJzHorq9EOx1u)YYm&1dTQ zF-Z9LU_41Cen<*r!H?##G{@fJIKs1gA29&duXJF@DQ9TihQrB&B|?;b3hz-^r0_n4 zaRO1U-W;VaqP17vp|FUrg^!r85nBEPPY8a@b$tW{e7!5pE7MeP5!>YudGii<#0P?kuj@$||RN_l0Lj;#5ddZK=Zln max_chars: + chunks.append(current.strip()) + current = sent + else: + current = f"{current} {sent}" if current else sent + if current.strip(): + chunks.append(current.strip()) + return chunks or [script] + def create_video(self, script, *, avatar_id=None, title="EnCoach Video", language="en"): """Create an avatar video from a script. + Automatically splits long scripts into multiple slides to stay under + ELAI's 60-second-per-slide limit. + Returns: dict with 'video_id', 'status' """ @@ -85,39 +112,43 @@ class ElaiService: avatar_code = avatar_preset["code"] avatar_src = f"https://elai-avatars.s3.us-east-2.amazonaws.com/common/{avatar_code.replace('.', '/')}/{avatar_code.replace('.', '_')}.png" + chunks = self._split_script(script) + slides = [] + for idx, chunk in enumerate(chunks, 1): + slides.append({ + "id": idx, + "speech": chunk, + "language": "English", + "voice": avatar_preset["voice"], + "voiceType": "text", + "voiceProvider": "azure", + "avatar": { + "code": avatar_code, + "gender": avatar_preset["gender"], + }, + "canvas": { + "version": "4.4.0", + "background": "#ffffff", + "objects": [ + { + "type": "avatar", + "left": 151.5, + "top": 36, + "fill": "#4868FF", + "scaleX": 0.3, + "scaleY": 0.3, + "height": 1080, + "width": 1080, + "src": avatar_src, + } + ], + }, + }) + _logger.info("ELAI: creating video with %d slide(s) from %d chars", len(slides), len(script)) + payload = { "name": title, - "slides": [ - { - "id": 1, - "speech": script, - "language": "English", - "voice": avatar_preset["voice"], - "voiceType": "text", - "voiceProvider": "azure", - "avatar": { - "code": avatar_code, - "gender": avatar_preset["gender"], - }, - "canvas": { - "version": "4.4.0", - "background": "#ffffff", - "objects": [ - { - "type": "avatar", - "left": 151.5, - "top": 36, - "fill": "#4868FF", - "scaleX": 0.3, - "scaleY": 0.3, - "height": 1080, - "width": 1080, - "src": avatar_src, - } - ], - }, - } - ], + "slides": slides, "tags": ["encoach"], } t0 = time.time() @@ -135,7 +166,7 @@ class ElaiService: # Step 2: trigger rendering -- ELAI requires a separate render call try: render_resp = _requests.post( - f"{ELAI_BASE}/videos/{video_id}/render", + f"{ELAI_BASE}/videos/render/{video_id}", headers=self._headers(), timeout=30, )