Files
encoach_be_odoo19/encoach_ai_media/services/elai_service.py
Talal Sharabi f5b627256f EnCoach Odoo 19 custom modules
Full backend implementation with custom Odoo modules:
- encoach_api: Core API, user management, JWT auth
- encoach_exam: Exam generation (reading, writing, listening, speaking)
- encoach_evaluate: AI-powered evaluation (writing, speaking)
- encoach_training: Training tips and walkthrough
- encoach_storage: File storage management
- encoach_payment: Stripe, PayPal, Paymob integration
- encoach_mail: Email notifications

Made-with: Cursor
2026-03-14 16:46:46 +04:00

108 lines
3.1 KiB
Python

import logging
import httpx
_logger = logging.getLogger(__name__)
ELAI_BASE_URL = "https://apis.elai.io/api/v1/videos"
class EncoachElaiService:
def __init__(self, env):
self.env = env
self.token = (
env["ir.config_parameter"]
.sudo()
.get_param("encoach.elai_token", "")
)
def _headers(self):
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json",
}
def create_video(self, text, avatar_code):
"""Create and render an ELAI video.
Returns the video_id for status polling, or None on failure.
"""
avatar = (
self.env["encoach.elai.avatar"]
.sudo()
.search([("avatar_code", "=", avatar_code)], limit=1)
)
if not avatar:
_logger.error("Avatar not found: %s", avatar_code)
return None
payload = {
"name": f"EnCoach Speaking - {avatar.name}",
"slides": [
{
"avatar": {
"code": avatar.avatar_code,
"canvas": avatar.canvas or "default",
"voiceId": avatar.voice_id or "",
"voiceProvider": avatar.voice_provider or "",
},
"speech": text,
},
],
}
try:
resp = httpx.post(
ELAI_BASE_URL,
headers=self._headers(),
json=payload,
timeout=30,
)
resp.raise_for_status()
data = resp.json()
video_id = data.get("_id") or data.get("id")
self._render_video(video_id)
return video_id
except Exception:
_logger.exception("ELAI video creation failed")
return None
def _render_video(self, video_id):
try:
httpx.post(
f"{ELAI_BASE_URL}/render/{video_id}",
headers=self._headers(),
timeout=30,
)
except Exception:
_logger.exception("ELAI render request failed for %s", video_id)
def poll_status(self, video_id):
"""Poll ELAI for video rendering status.
Returns dict with 'status' and optionally 'url' keys.
"""
try:
resp = httpx.get(
f"{ELAI_BASE_URL}/{video_id}",
headers=self._headers(),
timeout=30,
)
resp.raise_for_status()
data = resp.json()
status = data.get("status", "unknown")
result = {"status": status}
if status == "ready":
result["url"] = data.get("url", "")
return result
except Exception:
_logger.exception("ELAI status poll failed for %s", video_id)
return {"status": "error"}
def get_avatars(self):
"""List all configured ELAI avatars."""
avatars = self.env["encoach.elai.avatar"].sudo().search([])
return [a.to_encoach_dict() for a in avatars]