Skip to main content

Merge

Combine several inputs into one output — concatenate videos, stitch audio, or build an image slideshow. merge() accepts 2–10 inputs; each unique file is uploaded once.

Audio formats​

Accepted
audio/aac · audio/flac · audio/mpeg · audio/ogg · audio/wavStable

🔴 merge does NOT accept audio/mp4, though compress and convert both do. That is the clearest case of why the audio sets are per-operation rather than global: a file you can compress is not necessarily one you can merge. Call getSchema() for the authoritative set at runtime.

File-first — combine in order​

files([...]).merge(...) is the shortest path: the list is the set of inputs being combined, and array order is play order.

const result = await client
.files(['./intro.mp4', './body.mp4', './outro.mp4'])
.merge({ transition: 'crossfade', crossfadeDuration: 0.5 })
.run({ maxWait: '10m' });

await result.toFile('full-video.mp4');

Chain operations after merge() and they run on the single merged output in the same job. For large clips, use submit() to upload and create the workflow without blocking on the encode — it returns a handle you wait on later.

Image merge (slideshow)​

Image merges build a video or GIF and require an output type:

const result = await client
.files(['./1.jpg', './2.jpg', './3.jpg'])
.merge({ mediaKind: 'image', output: 'video', durationPerImage: 2.0 })
.run({ maxWait: '10m' });

Merge options​

The SDK options are camelCase; each maps to the snake_case contract option (loopCount → loop_count). Options that do not apply to the resolved media kind are dropped automatically.

OptionValuesDefaultApplies to
outputgif · video— requiredImage. outputType is accepted too.
delay10–5000 ms between frames500Image, GIF output.
loopCount0–1000 (forever)Image, GIF output.
durationPerImage0.5–30 seconds3Image, video output.
videoFormatmp4 · webmmp4Image, video output.
transitionsee belownoneImage (video output), video, audio.
transitionDuration0.1–5 seconds0.5Image, video output.
crossfadeDurationvideo 0.1–5, audio 0–10 secondsvideo 1, audio 0With transition: 'crossfade'.
gapDuration0–10 seconds0Audio — silence between tracks.
normalizeAudiobooleantrueVideo, audio.
reEncodeModeauto · always · neverautoVideo.
codech264 · h265 · vp9 · av1h264Video, when re-encoding. av1 is beta.
crf / preset0–51 / ultrafast … veryslow23 / mediumVideo, when re-encoding.
targetResolution'WxH', e.g. '1920x1080'inputs' sizeVideo, when re-encoding.
targetSizebytes, ≥ 1 MiB—Video, when re-encoding. Planned for long-form input.

"When re-encoding" means reEncodeMode auto or always; the server validates that dependency. For image slideshows fps is planned — output is 30 fps.

Transitions. Audio takes none or crossfade. Video and slideshows take none, fade, fadeblack, fadewhite, dissolve, crossfade and wipeleft / wiperight / wipeup / wipedown; further beta and experimental effects are listed by getSchema(). custom, wipetl, wipetr and wipebl are planned.

Operation-first — play order and per-clip options​

client.merge(...) takes a declared set of assets. Add .sequence(...) to set the play order separately — an asset may repeat, and is still uploaded once — and wrap an entry in clip(...) to give that position its own options:

Clip optionRangeApplies to
transitionas aboveVideo, audio — the join before this clip.
crossfadeDurationvideo 0.1–5, audio 0–10 secondsVideo, audio — with transition: 'crossfade'.
gapDuration0–10 secondsAudio only — dropped on a video merge.
trimStart≥ 0 secondsVideo, audio — cut from the start of this clip, before any transition.
trimEnd≥ 0 secondsVideo, audio — cut from the end of this clip, before any transition.

The upper bound of a trim is the clip's own duration, enforced when the job runs rather than before upload.

import { asset, clip } from '@giveitsmaller/sdk';

const intro = asset('./intro.mp4');
const body = asset('./body.mp4');

const result = await client
.merge(intro, body, { mediaKind: 'video', transition: 'crossfade' })
.sequence(
intro,
clip(body, { transition: 'none', trimStart: 2, trimEnd: 1.5 }),
clip(intro, { transition: 'fade' }),
)
.run({ maxWait: '20m' });

Every declared asset must appear in the sequence, and every sequenced asset must be declared. Image merges take no per-clip options. Each mistake throws before upload — see configuration and merge errors.

See also​

  • Archive — bundle files into a zip instead of merging them.
  • Errors — handling failures.