fix: correct ELAI API payload format for video generation
Use proper slide structure with canvas, avatar object, and voice settings to match ELAI API requirements. Made-with: Cursor
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -12,6 +12,14 @@ except ImportError:
|
|||||||
|
|
||||||
ELAI_BASE = "https://apis.elai.io/api/v1"
|
ELAI_BASE = "https://apis.elai.io/api/v1"
|
||||||
|
|
||||||
|
AVATAR_PRESETS = {
|
||||||
|
"vadim": {"code": "vadim.casual", "gender": "male", "voice": "en-GB-RyanNeural"},
|
||||||
|
"gia": {"code": "gia.casual", "gender": "female", "voice": "en-US-AriaNeural"},
|
||||||
|
"zara": {"code": "zara.regular", "gender": "female", "voice": "en-US-JennyNeural"},
|
||||||
|
"mason": {"code": "mason.casual", "gender": "male", "voice": "en-US-GuyNeural"},
|
||||||
|
"default": {"code": "gia.casual", "gender": "female", "voice": "en-US-AriaNeural"},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ElaiService:
|
class ElaiService:
|
||||||
"""Generate avatar videos for listening exercises and instructional content."""
|
"""Generate avatar videos for listening exercises and instructional content."""
|
||||||
@@ -33,6 +41,7 @@ class ElaiService:
|
|||||||
return {
|
return {
|
||||||
"Authorization": f"Bearer {self._get_token()}",
|
"Authorization": f"Bearer {self._get_token()}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _log(self, action, latency, status="success", error=None):
|
def _log(self, action, latency, status="success", error=None):
|
||||||
@@ -47,6 +56,14 @@ class ElaiService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _resolve_avatar(self, avatar_id):
|
||||||
|
if not avatar_id or avatar_id == "default":
|
||||||
|
return AVATAR_PRESETS["default"]
|
||||||
|
key = avatar_id.split(".")[0].lower() if "." in avatar_id else avatar_id.lower()
|
||||||
|
if key in AVATAR_PRESETS:
|
||||||
|
return AVATAR_PRESETS[key]
|
||||||
|
return {"code": avatar_id, "gender": "female", "voice": "en-US-AriaNeural"}
|
||||||
|
|
||||||
def list_avatars(self):
|
def list_avatars(self):
|
||||||
"""List available ELAI avatars."""
|
"""List available ELAI avatars."""
|
||||||
if not _requests:
|
if not _requests:
|
||||||
@@ -63,15 +80,45 @@ class ElaiService:
|
|||||||
"""
|
"""
|
||||||
if not _requests:
|
if not _requests:
|
||||||
raise RuntimeError("requests package not installed")
|
raise RuntimeError("requests package not installed")
|
||||||
|
|
||||||
|
avatar_preset = self._resolve_avatar(avatar_id)
|
||||||
|
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"
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"name": title,
|
"name": title,
|
||||||
"slides": [
|
"slides": [
|
||||||
{
|
{
|
||||||
|
"id": 1,
|
||||||
"speech": script,
|
"speech": script,
|
||||||
"avatar": avatar_id or "default",
|
"language": "English",
|
||||||
"language": language,
|
"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,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"tags": ["encoach"],
|
||||||
}
|
}
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
try:
|
try:
|
||||||
@@ -85,6 +132,15 @@ class ElaiService:
|
|||||||
data = resp.json()
|
data = resp.json()
|
||||||
self._log("create_video", int((time.time() - t0) * 1000))
|
self._log("create_video", int((time.time() - t0) * 1000))
|
||||||
return {"video_id": data.get("_id", data.get("id")), "status": data.get("status", "pending")}
|
return {"video_id": data.get("_id", data.get("id")), "status": data.get("status", "pending")}
|
||||||
|
except _requests.exceptions.HTTPError as exc:
|
||||||
|
body = ""
|
||||||
|
try:
|
||||||
|
body = exc.response.text[:500]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_logger.error("ELAI create_video failed: %s — %s", exc, body)
|
||||||
|
self._log("create_video", int((time.time() - t0) * 1000), "error", f"{exc} | {body}")
|
||||||
|
raise RuntimeError(f"ELAI API error: {exc.response.status_code} — {body}") from exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._log("create_video", int((time.time() - t0) * 1000), "error", str(exc))
|
self._log("create_video", int((time.time() - t0) * 1000), "error", str(exc))
|
||||||
raise
|
raise
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user