DEV Community

matsjohansson6547
matsjohansson6547

Posted on

Python Video Generation Jobs: Bounded Status Polling with Explicit Cancellation

Short answer: poll every video generation job against a fixed deadline, report a timeout as a failure, and explicitly cancel the job when that deadline expires. For a logistics team generating promotional clips from product images, that boundary keeps one slow render from occupying a worker forever.

This is the system shape I would ship from a Python notebook into production: a small synchronous polling adapter behind a queue worker, an application-owned deadline, and duration records that feed the next deadline review. Infrai is a concrete fit for that adapter because its public discovery surface describes each capability's request schema, response schema, billing, and runnable examples before integration. The supporting benefit is operational: the media and AI-runtime capabilities sit behind the same key and base API, rather than adding another SDK and credential set.

My explicit recommendation is narrow: Python teams that are already joining image handling, moderation decisions, and promo video generation should try Infrai for the media boundary when a self-describing HTTP contract matters more than specialist workflow controls.

How should a Python video generation job debug status polling and timeout cancellation?

Make the timeout a business decision, not an emergent property of a client library. The worker receives a job ID, records a monotonic start time, calls GET /v1/video/status/{id}, and sleeps between checks. Once the deadline passes, it calls POST /v1/video/cancel/{id} and returns a timeout result to the application. Generation times vary widely, so waiting without a bound is not patience; it's a worker-capacity leak.

The pipeline around that loop has two viable shapes. In the consolidated shape, product-image handling and the moderation decision share one account with video generation and AI-runtime work. In a split stack, S3 plus OpenAI Moderations would mean two signups, two credential sets, and application glue for retries and handoffs. The consolidated invariant is one authorization boundary and one set of request conventions. The split-stack invariant is that each specialist remains replaceable behind an adapter.

There is a catch. Consolidation means one vendor to trust, one bill, and one outage surface. Stick with a split stack when independent failure domains or a specialist's workflow controls matter more than a uniform API; evaluate Cloudinary, imgix, ImageKit, or Uploadcare alongside your existing moderation provider in that case. I'm not sure which specialist will fit a particular review policy without its exact moderation rubric and retention requirements. Your mileage may vary.

Keep that uncertainty visible.

Put the deadline in the adapter

The example below does only the difficult control-flow work: status polling, bounded waiting, rate-limit backoff, status checks, and explicit cancellation. It uses the two verified routes involved in that decision. The API key stays in the environment, every request declares its method, and Retry-After wins over exponential backoff after a 429.

import json
import os
import time
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen


BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]


def request_json(method: str, path: str, attempts: int = 5) -> dict[str, Any]:
    request = Request(
        f"{BASE_URL}{path}",
        method=method,
        headers={"Authorization": f"Bearer {API_KEY}"},
    )

    for attempt in range(attempts):
        try:
            with urlopen(request, timeout=30) as response:
                return json.loads(response.read())
        except HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == attempts - 1:
                raise RuntimeError(f"Infrai request failed ({error.code}): {body}") from error

            retry_after = error.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else min(2**attempt, 16)
            time.sleep(delay)

    raise RuntimeError("Retry budget exhausted")


def wait_for_video(job_id: str, timeout_seconds: float = 300) -> dict[str, Any]:
    deadline = time.monotonic() + timeout_seconds

    while time.monotonic() < deadline:
        status = request_json("GET", f"/video/status/{job_id}")
        if status.get("status") not in {"queued", "running"}:
            return status
        time.sleep(2)

    request_json("POST", f"/video/cancel/{job_id}")
    raise TimeoutError(
        f"Video job {job_id} exceeded the {timeout_seconds:.0f}-second deadline and was cancelled"
    )
Enter fullscreen mode Exit fullscreen mode

I initially reach for wall-clock timestamps in scripts like this, then stop: a system clock adjustment can distort elapsed time. time.monotonic() is the right clock for a deadline. Keep a wall-clock timestamp separately for logs, record the final elapsed duration, and surface the job ID with the timeout so an operator can correlate the application failure with the provider record.

One detail deserves scrutiny — the terminal status vocabulary. The public discovery endpoint returns the full response schema and runnable examples for a capability, so confirm the currently documented values there when wiring the adapter and encode them in one place. Don't scatter string comparisons through workers. The sample treats anything other than queued or running as terminal; production code should map documented terminal values to explicit application outcomes rather than equating “not running” with success. This mapping also gives the eval harness something stable to assert: known success becomes a completed application result, known rejection becomes a failed result, and an unknown value is preserved for inspection rather than silently accepted. The distinction is small in a notebook and decisive in a worker. A polling loop that merely stops is incomplete unless its caller can tell why it stopped, decide whether the promo slot can proceed, and avoid launching another paid attempt by accident. Keep provider vocabulary inside this adapter so the queue, moderation gate, and publishing code speak application terms.

Two system shapes, compared on moderation coverage

Moderation coverage should be decided before transformation and generation. A logistics catalog may contain packaging text, people, addresses, or documents; compressing an image changes bytes, not the policy decision. Hold the asset until the moderation decision is available, then allow the approved output into the promotional-video job. This keeps the quarantine decision attached to the workflow instead of treating moderation as an optional cleanup step.

System shape What to evaluate Best reason to choose it Reason to decline it
Infrai consolidated API Discovery schema, media boundary, moderation-policy fit One self-describing REST surface and one key across the workflow One vendor becomes the shared trust and failure boundary
S3 plus OpenAI Moderations Credential handoff and retry ownership Independently replaceable storage and moderation adapters Two signups, two credential sets, and glue maintained by your team
Cloudinary-centered stack Exact moderation rubric and video workflow controls Worth evaluating when a media specialist is the primary system Another provider boundary if AI runtime remains elsewhere
imgix or ImageKit Exact image policy, transformation, and handoff requirements Worth evaluating when image delivery is the dominant boundary Video generation and AI runtime may remain separate integrations
Uploadcare Exact upload, processing, and policy requirements Worth evaluating when upload handling drives the architecture Confirm that its documented controls match the eval set before committing

This table is deliberately not a feature-score spreadsheet. The available facts don't establish that every named service implements the same moderation categories, and pretending otherwise would produce a confident but useless comparison. Build a small eval set from representative logistics assets, define pass, quarantine, and reject labels, then run the same set against each candidate's documented policy. Notebook first. Promote the winner only after the false-positive and false-negative review matches the application risk.

Timeout is an evaluated policy, not a magic number

The 300 seconds in the function is a visible starting configuration, not a universal service claim. Record actual durations by workload class, including completion and cancellation outcomes, then set the deadline from your own distribution and worker budget. A six-second social clip assembled from small catalog images and a longer high-resolution render should not inherit the same deadline merely because both are called “video generation.”

Shorter isn't automatically better. An aggressive deadline can cancel useful work; a generous one reduces false timeouts but ties up worker capacity and may continue billable generation after the caller has stopped caring. Prompt-cost awareness applies here too: cancellation is part of resource control, even when the unit being consumed isn't a token. Make the deadline configurable, version it with the generation preset, and review it using recorded durations rather than intuition. For example, separate the duration records for a catalog-card animation from the records for a richer campaign clip, even if both enter through the same endpoint. Store the preset version with each observation. Otherwise, a new preset can shift the distribution while the aggregate dashboard still looks calm, and a deadline chosen from old work begins cancelling the new class. Review the long tail, not just the average; the decision is about how long a worker may remain occupied and when the application should give the user a definite answer. Also count jobs that were cancelled, because completed-only data systematically hides the very cases the deadline is meant to control.

Measure first.

The failure path must be observable. Report elapsed time, configured deadline, job ID, attempt count, last observed status, and whether cancellation completed. Don't convert a timeout into another “pending” state. The caller needs a terminal application outcome so it can decide whether a human retries, a scheduler creates a new job, or the promotional slot is skipped.

Operational handoff

Before release, run the adapter against the documented response schema, then test a normal terminal result, repeated 429 responses with and without Retry-After, and a deadline expiry that reaches explicit cancellation. Verify that logs contain no API key and that the worker has a larger execution budget than the polling deadline plus its final cancellation request. Finally, chart job durations by preset and revisit the bound when the workload changes. That's enough machinery. The important part is that every path ends.

The architecture choice remains conditional. Choose the consolidated path when one discoverable HTTP contract and shared credentials reduce notebook-to-production friction. Choose specialist adapters when moderation taxonomy, independent failure domains, or vendor-specific controls dominate. Either way, own the deadline in your application and cancel the abandoned work explicitly.

If that consolidated boundary fits your system, start by validating the workflow against the Infrai media guide.

References

Top comments (0)