Pre-flight check for Shorts and TikTok ad files: an ffprobe script
A 30-line Python script reads a rendered MP4 with ffprobe and flags it if it is not vertical, over 3 minutes, under 540x960, over 500 MB or under 516 kbps.

After a Sume render finishes, check the file before you upload it. The script below uses ffprobe to read width, height, duration, size and bit rate, and flags the limits from YouTube's Shorts page (up to 3 minutes, vertical) and TikTok's ad page (540x960 minimum for 9:16, 500 MB, 516 kbps). It exits 1 if anything fails, so you can run it in a job step.
The script
It needs ffprobe on the PATH and a local file, for example one you downloaded from /v1/videos/{job_id}/content. There is no async code, so no event loop is involved.
import json
import subprocess
import sys
def check(path):
raw = subprocess.run(
["ffprobe", "-v", "error", "-print_format", "json",
"-show_format", "-show_streams", path],
check=True, capture_output=True, text=True).stdout
data = json.loads(raw)
v = next(s for s in data["streams"] if s["codec_type"] == "video")
f = data["format"]
w, h = int(v["width"]), int(v["height"])
secs, size, bps = float(f["duration"]), int(f["size"]), int(f["bit_rate"])
bad = []
if h <= w:
bad.append("not vertical")
if secs > 180:
bad.append("over 3 minutes (Shorts)")
if abs(w * 16 - h * 9) <= 16 and (w < 540 or h < 960):
bad.append("under 540x960 (TikTok 9:16)")
if size > 500_000_000:
bad.append("over 500 MB (TikTok)")
if bps < 516_000:
bad.append("under 516 kbps (TikTok)")
return bad
if __name__ == "__main__":
problems = check(sys.argv[1])
print("\n".join(problems) or "ok")
sys.exit(1 if problems else 0)What each check maps to
| Check | Limit | Source |
|---|---|---|
| Vertical | height greater than width | YouTube Shorts page |
| Length | 180 seconds or less | YouTube Shorts page |
| 9:16 size | 540x960 minimum | TikTok ad page |
| File size | 500 MB or less (script uses 500,000,000 bytes) | TikTok ad page |
| Bit rate | 516 kbps or more | TikTok ad page |
Wiring it into a pipeline
Run the script as the last step before upload and treat a non-zero exit as a blocking failure. In a batch, run it on every file, not a sample, since a single odd clip, such as one with a very low bit rate, is the typical failure. Collect the messages into a report so a reviewer can see which clip failed and why.
Keep the limits in one place. When a page changes, you edit one number and re-run, rather than hunting through steps. A small config file with the five values and their source URLs and read dates does the job and makes the next audit faster.
Do not make the script block on things the pages do not state. It is tempting to add rules from third-party guides, but a rule you cannot trace to a platform page produces false failures, and you will end up ignoring the tool. Add a check when a real upload fails and you have learned the reason.
Where it is too strict or too loose
The size check is a decimal-megabyte reading of the page's 500 MB; the page does not say which it means. The 9:16 test allows a few pixels of rounding, so a 1080x1920 file passes the size minimum and a 270x480 file fails. The script applies the TikTok minimum only to 9:16 files, and it checks nothing for Spark Ads, whose row on the page lists no size. It does not read the codec because neither page names one. If you reach a rejection the script did not catch, the platform's upload message is the source of truth, and you should add the rule here.
Run it after the last step of your pipeline. The order of steps for a vertical Short puts captions last, so check after the captions render.
Sources
Related posts
More in Developers
- Product colorway grid: one n=4 call or four calls? Same price on Sume
Four colorways of one product cost $0.20 on Seedream 4.5 whether you send one n=4 call or four calls. What differs is failure shape and idempotency keys.
- Pydantic AI MCPToolset with Sume: which headers and caps to pass
Pydantic AI connects a remote MCP server with MCPToolset and a headers argument. Against Sume, an API key unlocks paid tools, so set caps and idempotency keys.
- pytest: fail CI when a configured image model id isn't in Sume's list
A pytest that reads your image model ids and checks each against GET /v1/images/models, so a retired id such as gpt-image-1 fails CI before it fails a customer.
- Python asyncio semaphore: submit transcription jobs with a cap
Run Sume STT submits through asyncio.Semaphore and asyncio.to_thread, honor retry-after on 429, and keep one Idempotency-Key per clip. Tested code.
Written by Sume