Genace

Video generation API download validation: verify bytes before publishing

·

Video generation API download validation prevents a successful render from becoming a broken customer asset. An output URL may return a network error, a partial transfer, or an unexpected document. Treat HTTP success as one check in a delivery pipeline, not as proof that a playable video reached your storage.

Genace’s output contract includes url, type, and meta. The current Seedance adapter returns a video URL with an empty metadata object. Your application should therefore inspect the collected file if it needs a measured duration, actual dimensions, or confirmation that a video stream exists.

Validate the output manifest first

Only begin collection after state is succeeded. Check that outputs is an array and select items whose type is video. Parse each URL and apply your application’s destination policy. A backend downloader should avoid accepting arbitrary user-supplied URLs through the same path, and should apply its policy again to redirect destinations.

Use a staging filename until validation completes. Keep the source job identifier, output index, transfer timestamps, and error category in a manifest so a retry can continue delivery without creating a new generation.

Inspect the staged media

Save the Python validator below as validate_video.py in your working directory. Run the shell block as a standalone Bash script with Python 3, ffprobe, sha256sum, and curl 8.4.0 or newer installed. Older curl versions cannot enforce —max-filesize during transfers whose size is initially unknown, as explained in the curl manual.

The URL must already pass your destination policy. HTTPS restrictions and a redirect count do not enforce host or IP allowlists; automatic redirects here require an external egress policy covering every destination. Otherwise use a per-hop downloader. Do not send your Genace Authorization header to the media host. The 100 MiB limit and 120-second transfer budget are example local limits.

set -euo pipefail
: "${GENACE_OUTPUT_URL:?Select a succeeded job video output first}"
umask 077
stage=$(mktemp -d)
curl --fail --location --proto '=https' --proto-redir '=https' \
  --max-redirs 5 --max-time 120 --max-filesize 104857600 \
  --output "$stage/result.part" -- "$GENACE_OUTPUT_URL"
test -s "$stage/result.part"
ffprobe -v error -select_streams V:0 \
  -show_entries stream=codec_name,width,height \
  -show_entries format=duration,size -of json "$stage/result.part" > "$stage/inspection.json"
python3 validate_video.py "$stage/inspection.json"
sha256sum "$stage/result.part" > "$stage/result.sha256"
printf 'Validated staging directory: %s\n' "$stage"

Without failure stopping, a failed curl command can be followed by a successful hash of a previous result.part. Each attempt now has a private staging directory; transfer, inspection, or validation failure stops before the checksum. Keep failed staging files unavailable, inspect them privately, then remove them under your retention policy. Do not treat the presence of a directory as approval.

Make the inspection decision executable

ffprobe reports multimedia metadata; its successful exit does not require a selected video stream to exist. An audio-only WAV can return success with an empty streams list. This conservative local policy requires one selected video stream, positive integer dimensions, a named codec, and finite positive container duration. Uppercase V:0 excludes attached pictures, thumbnails, and cover art; lowercase v:0 can select an MP3 cover image instead of moving video:

import json
import math
import sys

def validate_video(report):
    if not isinstance(report, dict):
        raise ValueError("Expected an inspection object")
    streams = report.get("streams")
    if not isinstance(streams, list) or len(streams) != 1:
        raise ValueError("Expected one selected video stream")
    video = streams[0]
    if not isinstance(video, dict):
        raise ValueError("Invalid stream metadata")
    if not isinstance(video.get("codec_name"), str) or not video["codec_name"].strip():
        raise ValueError("Missing video codec")
    for field in ("width", "height"):
        if type(video.get(field)) is not int or video[field] <= 0:
            raise ValueError("Missing positive video dimensions")
    container = report.get("format")
    try:
        duration = float(container["duration"])
    except (KeyError, TypeError, ValueError):
        raise ValueError("Missing measured container duration") from None
    if not math.isfinite(duration) or duration <= 0:
        raise ValueError("Invalid container duration")

if __name__ == "__main__":
    with open(sys.argv[1], encoding="utf-8") as source:
        validate_video(json.load(source))

Missing duration means this policy cannot approve the file, not that every such container is corrupt. Metadata inspection does not decode every frame or prove complete playback. Compare dimensions and duration against your requested specification separately, then perform decoder and playback checks appropriate to your product. A content-type header cannot replace inspecting the bytes.

CheckReject whenRecovery
Transfer resultHTTP or network failureRetry collection with a bounded policy
File sizeEmpty or over local limitKeep unavailable and investigate
Media parserNo readable video streamPreserve diagnostic details
Requested specificationMaterial mismatchFlag for product review
Stored checksumDifferent from verified staging copyRepeat storage transfer

Preserve the right failure label

A failed download does not change the remote job’s succeeded state. Save a local delivery_failed status instead. That tells support whether the next action is another download, a storage repair, or a deliberate regeneration request with a new budget.

Avoid validating only the filename extension, publishing before ffprobe finishes, and assuming an interrupted file is usable because it has nonzero size. Also avoid uploading source signed URLs into public logs or analytics. Their query parameters can expose temporary access credentials even when your API key is absent.

Verification checklist

Use local fixtures for a valid video, audio-only media with and without cover art, an HTML error page renamed as video, an empty file, and a file truncated before its container metadata. Other truncations can still pass metadata probing, so test decoding separately. Inject transfer and parser failures with an old result.part present and verify no checksum is promoted. After upload, compare stored bytes with the verified copy and test playback through your authenticated asset route; a locally computed digest alone does not prove completeness or match a trusted expected source.

Continue with output storage, observability, and failed job responses.

Continue with production operations

Where these facts come from