Node stream.pipeline to save a Sume MP4 and catch truncation
Stream a finished Sume video to disk with stream.pipeline, then compare bytes written with content-length so a cut-off MP4 never reaches your users.

Do not buffer a 30-second video
A completed job from POST /v1/videos is fetched with GET /v1/videos/{id}/content?index=0. Reading that response into memory with res.arrayBuffer() works for a short clip and becomes a problem for longer or higher-resolution output, and Seedance 2.5 and Wan 3.0 reach 30 seconds.
Node's stream.pipeline connects the response body to a file and handles errors and cleanup of the streams in one call, so a failure mid-transfer rejects instead of leaving a dangling handle.
The script
Run with VIDEO_ID and SUME_API_KEY set, on Node 18 or newer, saved as dl.mjs. After the pipeline finishes it compares the file size to the content-length header when the server sent one.
import { createWriteStream } from "node:fs";
import { stat, rm } from "node:fs/promises";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
const url = "https://api.sume.com/v1/videos/" + process.env.VIDEO_ID + "/content?index=0";
const res = await fetch(url, {
headers: { Authorization: "Bearer " + process.env.SUME_API_KEY },
});
if (!res.ok) throw new Error("download failed: HTTP " + res.status);
await pipeline(Readable.fromWeb(res.body), createWriteStream("clip.mp4"));
const expected = Number(res.headers.get("content-length") || 0);
const { size } = await stat("clip.mp4");
if (expected && size !== expected) {
await rm("clip.mp4");
throw new Error("truncated: " + size + " of " + expected + " bytes");
}
console.log("saved", size, "bytes");Limits of the check
- A chunked response has no content-length; then verify with ffprobe before you serve the file.
- Only download after status completed; before then the route returns an error body, which the res.ok check turns into an exception.
- On failure, retry the download, not the job: the generation already finished and was billed once.
Sources
Related posts
More in Developers
- Gemini Omni edit input is capped at 10 seconds: trim first on Sume
Google says Omni edit inputs must be 10 seconds or less. Cut the clip with Sume's video-trim at $0.02 a job, then send the short clip to the edit request.
- Gemini Omni audio is always on: generate_audio false returns 400
Sume's Omni row rejects generate_audio false with a 400 because audio is native and always on. To ship a silent clip, drop the track with video-trim after.
- Omni edit: one change per turn or one combined prompt, and the cost
Three single-change Omni edits on a 10 s clip are $3.75 at 720p; one combined prompt is $1.25. Per-turn is 3x the price, so pick by how a failed edit costs you.
- Gemini Omni 40-second extension: chain 10-second clips on Sume
Google extends Omni scenes in 10-second steps up to 40 seconds. Sume's catalog shows no extend field, so chain clips with a last-frame image instead.
Written by Sume