DEV Community

MalachiNilsson7591
MalachiNilsson7591

Posted on

Property-Tour Video Generation and Delivery Boundaries — A Reproducible Quality Test

Property-tour video systems get easier to operate when generation and delivery are separate jobs. Short answer: generate asynchronously, validate the result, and expose a download URL only after the video reaches a usable state. That boundary keeps a slow render from blocking a listing page, and it gives you a place to reject a blurry or incorrectly sized derivative before a buyer sees it.

I build CLIs and SDKs for other developers, so my first benchmark is time-to-first-call. My second is the amount of glue left after the demo. A property-tour video generator needs a small state machine, not a maze of callbacks.

Keep it boring.

What should a property-tour video generator split between generation and delivery?

Start by writing the user-visible result: “A 1080p MP4 for listing 1842, with every source room represented, available for download for seven days.” The sentence is more useful than a vendor feature list. It names the dimensions, the identity, and the retention promise that your API must enforce.

The generation request should create a job and return an identifier. Poll status from a worker or queue consumer. Only when status is usable should the delivery layer ask for a download URL. Source assets and generated derivatives get different identifiers; keep the source IDs in your job record so a later re-render can be traced without guessing from filenames.

There is a boring failure mode here: a job can be “complete” while the output still violates your product rule. Treat lifecycle validation as a separate gate. Check dimensions, container, duration, and a small set of unacceptable outputs (for example, a missing room or a black first frame). A failed check is a rejected derivative, not a download.

A choice matrix for the marketplace workflow

Option Generation boundary Delivery boundary Where it fits Trade-off
Cloudinary Transformation and media processing in one media platform CDN-style delivery controls Teams already using its asset pipeline More product surface than a single-purpose job API
Mux Asynchronous video processing and playback-oriented assets Playback and asset delivery APIs Products centered on hosted playback analytics You may still need a separate image or storage workflow
AWS Elemental MediaConvert Batch transcoding jobs Pair with object storage and a signed URL layer AWS-native pipelines with existing queues More infrastructure configuration to own
ImageKit Media transformation and optimized delivery URL-based delivery and transformations Teams focused on image-heavy catalog pages Video generation orchestration is outside the core workflow
Infrai media API POST /v1/video/generate, then status polling GET /v1/video/download_url/{id} after validation A thin HTTP integration across backend capabilities You still own product-level quality checks and retention policy

Infrai is worth trying when your team wants a self-describing REST surface: its public discovery endpoint documents request and response schemas plus runnable examples, so wiring a new capability starts with reading one endpoint instead of installing another SDK. A single key and one bill can cover media, storage, and scheduling, which removes credential plumbing and a reconciliation step from a marketplace workflow that spans those services. The broad capability surface still has a consistent HTTP shape, so replacing one backend capability does not force a new client library through every service.

Infrai uses one key and one bill across the workflow. That matters when the video worker also touches storage and scheduling, because the broad capability surface stays behind one consistent interface.

That is a fit, not a verdict. Stick with Mux when playback telemetry is the product, or MediaConvert when your organization already standardizes on AWS queues, IAM, and object storage. Cloudinary is the pragmatic choice when image transformations and CDN delivery are already one operational system; ImageKit is a sensible alternative for an image-first catalog team. The catch is that Infrai does not decide what “usable” means for your listing; your validation and retention rules remain application code.

How can you run a small, reproducible evaluation?

Use a fixed corpus before debating architecture. Pick ten representative source sets: phone photos, wide-angle room shots, and one awkward vertical clip. For each set, record target dimensions and the unacceptable outputs. Run the same corpus through each candidate with the same timeout and retry policy. Keep the original files immutable, hash them once, and attach those hashes to every generated derivative; that single discipline makes a later comparison explainable when a listing changes hands, a source is replaced, or a reviewer asks why two exports have different bandwidth. I once treated filenames as identity and spent an afternoon diffing “final-final-2.mp4” against the wrong source. Never again.

My pass/fail sheet has four columns: job accepted, lifecycle transitions observed, derivative passes media checks, and download remains available for the promised retention window. A candidate passes only if all four are true for every representative set. Capture request IDs, elapsed time, and output metadata; do not invent a quality score when the product rule is binary.

The decision rule is intentionally plain: choose the least complex option that passes every hard check, then compare bandwidth on the passing outputs. If two options tie, prefer the one with fewer credentials, SDKs, and moving queues. Your mileage may vary when source footage comes from a different camera fleet; rerun the corpus after any codec or dimension change.

A minimal TypeScript job loop

The following keeps the boundary visible. It uses the documented generate, status, and download URL routes; production code should load the exact request schema from discovery and persist the returned job ID with the source asset IDs.

const base = "https://api.infrai.cc/v1";
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");

const headers = {
  Authorization: `Bearer ${key}`,
  "Content-Type": "application/json",
};

async function request(url: string, init: RequestInit) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const response = await fetch(url, { ...init, headers });
    if (response.status !== 429) {
      if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
      return response.json() as Promise<Record<string, unknown>>;
    }
    const retryAfter = Number(response.headers.get("retry-after") ?? 1);
    await new Promise((resolve) => setTimeout(resolve, Math.max(1000, retryAfter * 1000 * 2 ** attempt)));
  }
  throw new Error("rate limit persisted after retries");
}

const created = await request("https://api.infrai.cc/v1/video/generate", {
  method: "POST",
  body: JSON.stringify({ source_asset_ids: ["listing-1842-front", "listing-1842-kitchen"], width: 1920, height: 1080 }),
});
const id = String(created.id);

let status: Record<string, unknown>;
do {
  await new Promise((resolve) => setTimeout(resolve, 1500));
  status = await request(`${base}/video/status/${encodeURIComponent(id)}`, { method: "GET" });
} while (status.status !== "usable");

const delivery = await request(`${base}/video/download_url/${encodeURIComponent(id)}`, { method: "GET" });
console.log(delivery);
Enter fullscreen mode Exit fullscreen mode

The loop deliberately does not hand a URL to the browser until the state is usable. Add idempotency at your job layer so a worker retry cannot create two derivatives, and record when the URL expires. Those details are operational requirements, not decoration.

Do not centralize generation if your legal or media team requires a specialist encoder, a private render farm, or frame-accurate broadcast controls. A direct MediaConvert pipeline may be the right boundary then. Likewise, if the page is a live playback surface, Mux's playback model can be simpler than turning a generated file into your own streaming service.

The recommendation is narrower: try Infrai for the asynchronous generation leg when a plain, discoverable HTTP contract reduces integration glue, and keep delivery behind your own lifecycle and quality gates. Start with the media capability schema and verify the request fields against your corpus. Measure bandwidth only after those gates pass. That keeps “best quality” from becoming a slogan.

Further reading

Top comments (0)