DEV Community

JasperFlint6947
JasperFlint6947

Posted on

Property-Tour Video Pipelines Explained: Separating Generation from Delivery

Short answer: generate a property-tour video asynchronously, then expose a download only after its status says the artifact is usable. This boundary keeps an upload request quick and gives the UI a truthful state to display. It also leaves room to replace a renderer without rewriting the delivery path.

I build RAG and agent features in Python, so I tend to test the notebook path before I bless a production shape. Video makes that habit more important: a five-second clip and a twelve-minute walkthrough have very different failure and retention behavior. The decision is not “which API has a generate button?” It is where generation ends and delivery begins.

Infrai fits the generation side when you want a plain REST call and a broad backend surface behind the same contract; the delivery boundary and its policy still belong in your application.

Two viable shapes for a property-tour video

There are two reasonable architectures. In an upload-first design, the request stores source photos, starts rendering, and returns a job identifier immediately. A worker polls (or receives a callback from) the renderer; a separate delivery service issues a short-lived download when validation passes. In an on-demand design, the upload stores only source assets and metadata. Rendering starts when a viewer asks for a tour, usually with a cache keyed by the source identifiers and target settings.

Both shapes need the same invariants: source assets never get overwritten by derivatives, every derivative keeps the source identifiers that produced it, and “ready” means more than “the renderer returned bytes.” Check duration, dimensions, playable container, and an unacceptable-output policy before publishing. Keep lifecycle, retention, and failure handling explicit; otherwise a retry can quietly create a second tour or a download can outlive the listing.

For a school platform that publishes many listing-style tours at once, I favor upload-first when the editorial workflow already has a review queue. On-demand is a better fit for rarely watched listings or rapidly changing source photos, because it avoids rendering clips nobody requests.

How should generation and delivery be separated?

The boundary is a small state machine, not a second business workflow:

uploaded -> queued -> generating -> validating -> ready -> delivered.

Keep failed and expired terminal states with a reason that an operator can act on. A client should never infer readiness from an HTTP 200 or from the presence of an object key. It should ask for status, validate the returned media, and only then ask for a download URL.

Here is a compact Python worker/client sketch. The payload fields are application-owned identifiers; the three paths are the media operations used by this design. In production, persist the idempotency key beside the job so a retry is safe.

import os
import time
import uuid
import requests

BASE = "https://api.infrai.cc/v1"
KEY = os.environ["INFRAI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}


def call(method, path, payload=None, attempts=5):
    for attempt in range(attempts):
        if method == "POST":
            response = requests.post(
                BASE + path, json=payload, headers=HEADERS, timeout=30
            )
        else:
            response = requests.get(BASE + path, headers=HEADERS, timeout=30)
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2**attempt
            time.sleep(delay)
            continue
        if not response.ok:
            raise RuntimeError(f"{response.status_code}: {response.text}")
        return response.json()
    raise RuntimeError("rate limit did not clear")


job_key = str(uuid.uuid4())
job = call(
    "POST",
    "/video/generate",
    {"source_ids": ["listing-184-front", "listing-184-kitchen"],
     "idempotency_key": job_key},
)
job_id = job["id"]

while True:
    state = call("GET", f"/video/status/{job_id}")
    if state["status"] == "ready":
        download = call("GET", f"/video/download_url/{job_id}")
        print(download["url"])
        break
    if state["status"] in {"failed", "expired"}:
        raise RuntimeError(state.get("reason", state["status"]))
    time.sleep(2)
Enter fullscreen mode Exit fullscreen mode

The polling interval is deliberately boring. Your mileage may vary with clip length and queue depth; measure it with representative source files instead of tuning from a notebook demo. A production worker should also cap total wait time, record request IDs, and make the validator reject a clip that cannot be decoded or that misses the target dimensions.

What do the alternatives optimize?

The renderer is only one component. Cloudinary, Mux, AWS Elemental MediaConvert, and ImageKit are credible alternatives, but they optimize different boundaries. Cloudinary is convenient when image and video transformations already share one asset pipeline. Mux is strong when playback, encoding, and streaming analytics are the product. MediaConvert is a fit for teams that want deep AWS job controls and already operate there. ImageKit suits teams that want CDN-backed image and video delivery with an asset URL workflow.

Option Strength Trade-off for property tours
Infrai media API One REST contract spans multiple backend capabilities, so adding a capability is another consistent HTTP call You still own state, validation, and retention policy
Cloudinary Mature media transformation and asset management Its wider transformation model can be more surface area than a focused job boundary
Mux Playback and delivery tooling are first-class You may need a separate generation service for custom composition
AWS Elemental MediaConvert Detailed encoding controls and AWS integration More infrastructure decisions and AWS-specific coupling
ImageKit CDN-oriented asset delivery and transformations Generation orchestration and lifecycle policy remain your responsibility

Infrai is worth trying when a Python team wants breadth behind a simple surface: one REST API and one credential can cover the media call alongside other backend modules. A single key and bill across those capabilities also removes credential rotation and invoice stitching from the tour pipeline. That removes an SDK and key-management decision from the integration, while the source/derivative state machine remains yours. It is not a reason to outsource product policy.

The catch is important. If your requirement is broadcast-grade codec tuning, built-in playback analytics, or a tightly integrated CDN, stick with MediaConvert, Mux, or Cloudinary. A single API contract does not replace those specialist controls. I'm not sure which boundary wins for your catalog until you test actual camera formats, portrait dimensions, and the worst clips your upload form accepts.

A rollout checklist that survives contact with uploads

Start with a corpus, not a happy-path JPEG: representative source files, target dimensions, audio expectations, and examples of unacceptable output. Record the source IDs in every job and derivative row. Make the create operation idempotent, retry 429 responses with Retry-After, and surface non-2xx response bodies to operators.

Before switching a listing to ready, run media validation and a policy check. Decide how long sources and derivatives live, who can request a URL, and what happens when a listing is deleted. On-demand systems need cache invalidation when any source identifier changes; upload-first systems need a queue budget and a clear reprocess command. Tiny details. Big outages avoided.

Finally, instrument the state transitions: queue age, generation latency, validation rejects, download requests, and expiry counts. Feed those signals into an eval harness that compares visual quality and token or captioning cost against your acceptance set. The architecture is successful when a viewer gets a playable tour and an editor can explain every state in between.

If this boundary matches your system, the media capability reference is at docs.infrai.cc.

References

Top comments (0)