Shorts season trailer: first 5 seconds of every episode in one clip

Build a Shorts season trailer by trimming 0 to 5 s from each episode with video trim, then planning one timeline render under a music bed. Python included.

6 min readSume
All posts

The answer in one paragraph

To make a trailer for a Shorts series, trim the first few seconds of each finished episode with video trim, then put the trimmed files into one Timeline 1.0 body as consecutive video[] slots. For a four-episode season at 5 seconds each that is four trim jobs at $0.02 each and one 20-second render, which reserves one whole output minute at $0.10 under the documented rates. Confirm both in GET /v1/catalog before you budget, because the docs defer to the live catalog.

The timing matters this month. The October platform roundup says YouTube's Shorts series, with seasons, episodes, custom thumbnails and sequential playback, began rolling out on 23 September across web, mobile and TV. A series needs a front door, and a trailer made from the episodes themselves is the cheapest one.

Why trailers are a timeline job and not a trim job

Trim returns one new MP4 from one clip. It does not assemble. Assembly is the timeline's job: one audio spine, up to 200 ordered video[] slots, and every URL must already be this workspace's media.sume.com artifact. That is why the code below uses the trimmed video_url values the trim jobs return, with source_in left at 0 because each trimmed file starts where you want it to.

Slot rules to respect: video[0].start must be 0, later starts must increase, and each slot needs a duration of at least 0.2 seconds. Coverage can stop at most 0.5 seconds before the spine ends, so make the last slot reach the end. If you add a transition it goes on every slot after the first, is capped at 1 second, and must fit inside half of the shorter neighbor.

Sound: one bed, not four episode soundtracks

The timeline has one audio spine. Sume's docs describe the spine as the audio of the output, and a clip's own track is not a documented input to the mix. For a trailer, the straightforward design is a declared-length silent spine plus a soundtrack bed. Silence mode takes audio.duration_seconds and no url, parts, gain_db or source_in. A soundtrack takes url, gain_db, loop and fade_out_seconds up to 10 seconds. Ducking (duck_db) needs a real spine, so it is not available under silence mode.

If you want episode dialogue in the trailer instead, build the spine from detached audio with timeline audio first. That is a different post and a different bill.

Budget by season length

The only variables are the number of episodes and seconds per cut. Trim jobs bill per job, not per second, and the render reserves per whole output minute.

Trailer cost from the documented rates, season of N episodes at 5 s each (read 2026-10-06)
EpisodesTrailer lengthTrim jobs at $0.02Render minutes at $0.10Total
420 s$0.081 min = $0.10$0.18
840 s$0.161 min = $0.10$0.26
1260 s$0.241 min = $0.10$0.34
1365 s$0.262 min = $0.20$0.46

Python: trim each episode, then plan the trailer

The script trims 0 to 5 seconds from four episodes with a stable idempotency key per episode, waits for each job, and asks the unbilled plan endpoint what the render will cost. Replace the example URLs with your own imported artifacts. To render, post the same body to /v1/timeline-1.0/render with its own Idempotency-Key.

import json, os, time, urllib.request

def sume(method, path, body=None, key=None):
    h = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
         "Content-Type": "application/json", "User-Agent": "sume-example/1.0"}
    if key:
        h["Idempotency-Key"] = key
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request("https://api.sume.com" + path, data, h, method=method)
    with urllib.request.urlopen(req) as r:
        return json.load(r)

def wait(job):
    jid = job["request_id"]
    while not (st := sume("GET", f"/v1/jobs/{jid}/status"))["terminal"]:
        time.sleep(st.get("next_poll_after_seconds") or 5)
    assert st["result_ready"], f"job {jid} ended without a result"
    res = sume("GET", f"/v1/jobs/{jid}/result")
    return res.get("result", res)

EPS = ["https://media.sume.com/artifacts/artf_demo/ep%d.mp4" % n for n in (1, 2, 3, 4)]
slots = []
for i, url in enumerate(EPS):
    job = sume("POST", "/v1/video-trim", {"video_url": url, "start": 0, "duration": 5},
               f"s1-trailer-trim-{i}")
    slots.append({"source_url": wait(job)["video_url"], "start": i * 5, "duration": 5})
body = {"audio": {"mode": "silence", "duration_seconds": 5 * len(EPS)}, "video": slots,
        "soundtrack": {"url": "https://media.sume.com/artifacts/artf_demo/bed.mp3",
                       "fade_out_seconds": 1}}
print(sume("POST", "/v1/timeline-1.0/plan", body)["billable_minutes"])

Watch the 12-to-13 episode edge

The last row of the table is the trap: 13 episodes at 5 seconds is 65 seconds, and the render reserves a second minute. If your season is 13 episodes, cut 4.5 seconds per episode and the trailer is 58.5 seconds again. Run the plan, read billable_minutes, and only then render. The plan does not create a job, reserve credits or download media, so you can run it as often as you like while you tune the cut length.

Two practical notes. First, the trim keys are s1-trailer-trim-0 through -3, so a retry after a dropped connection replays the same job instead of billing a duplicate; use the same key only for the same body. Second, trimming episodes you published weeks ago costs the same $0.02 as trimming new ones, so a trailer for season two can reuse season one's best five seconds without re-rendering anything.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume