File size: 2,502 Bytes
34b0226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bcfc897
 
 
 
 
 
 
 
34b0226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
"""Hands a render to the Remotion service with presigned upload URLs (it never holds storage keys)."""

import httpx

from app.config import settings
from app.models import Artifact, Job
from app.services.storage import keys, storage

COMPOSITIONS = {
    "AudiogramSquare": "mp4",
    "QuoteCard": "png",
    "CarouselSlide": "png",
}


class PermanentJobError(RuntimeError):
    """Fail the job now; retrying will not help."""


def available() -> bool:
    """Whether the render service answers right now (a quick health check, not a promise for later)."""
    try:
        return httpx.get(f"{settings.renderer_url}/health", timeout=2).status_code == 200
    except httpx.HTTPError:
        return False


def request_render(job: Job, artifact: Artifact, composition: str, props: dict | None = None,
                   stills: list[dict] | None = None) -> None:
    """Start a render. `stills` renders one PNG per props dict (carousels) instead of a single output."""
    fmt = COMPOSITIONS[composition]
    content_type = "image/png" if fmt == "png" else "video/mp4"
    body: dict = {
        "jobId": str(job.id),
        "compositionId": composition,
        "props": props or {},
        "format": fmt,
        "uploadContentType": content_type,
        "callbackUrl": f"{settings.api_url}/api/internal/jobs/{job.id}/progress",
    }
    if stills:
        outputs = []
        for i, still_props in enumerate(stills, start=1):
            key = keys.artifact(artifact.workspace_id, artifact.id, f"slide-{i:02d}", fmt)
            outputs.append({"props": still_props, "uploadUrl": storage.presign_put(key, content_type, ttl=6 * 3600),
                            "storageKey": key})
        body.update(stills=outputs, props=stills[0], uploadUrl=outputs[0]["uploadUrl"],
                    storageKey=outputs[0]["storageKey"])
    else:
        key = keys.artifact(artifact.workspace_id, artifact.id, composition.lower(), fmt)
        body.update(uploadUrl=storage.presign_put(key, content_type, ttl=6 * 3600), storageKey=key)
    try:
        resp = httpx.post(f"{settings.renderer_url}/render", headers={"x-internal-token": settings.internal_token},
                          json=body, timeout=30)
    except httpx.ConnectError as exc:
        raise PermanentJobError("Renderer is not running. Start it with: cd renderer && npm start") from exc
    if resp.status_code == 400:
        raise PermanentJobError(f"Renderer rejected the props: {resp.text[:400]}")
    resp.raise_for_status()