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/wav | Stable |
🔴 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.
- TypeScript
- PHP
- Python
- Rust
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');
use Gisl\Sdk\Ergonomic\MergeOptions;
$result = $client->files(['./intro.mp4', './body.mp4', './outro.mp4'])
->merge(new MergeOptions(transition: 'crossfade', crossfadeDuration: 0.5))
->run(maxWait: '10m');
$result->toFile('full-video.mp4');
Not available: the Python SDK is not published and no release is scheduled — see Python.
Not an application SDK: Rust is internal contract types — see Rust.
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:
- TypeScript
- PHP
- Python
- Rust
const result = await client
.files(['./1.jpg', './2.jpg', './3.jpg'])
.merge({ mediaKind: 'image', output: 'video', durationPerImage: 2.0 })
.run({ maxWait: '10m' });
use Gisl\Sdk\Ergonomic\MergeOptions;
$result = $client->files(['./1.jpg', './2.jpg', './3.jpg'])
->merge(new MergeOptions(mediaKind: 'image', output: 'video', durationPerImage: 2.0))
->run(maxWait: '10m');
Not available: the Python SDK is not published and no release is scheduled — see Python.
Not an application SDK: Rust is internal contract types — see Rust.
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.
| Option | Values | Default | Applies to |
|---|---|---|---|
output | gif · video | — required | Image. outputType is accepted too. |
delay | 10–5000 ms between frames | 500 | Image, GIF output. |
loopCount | 0–100 | 0 (forever) | Image, GIF output. |
durationPerImage | 0.5–30 seconds | 3 | Image, video output. |
videoFormat | mp4 · webm | mp4 | Image, video output. |
transition | see below | none | Image (video output), video, audio. |
transitionDuration | 0.1–5 seconds | 0.5 | Image, video output. |
crossfadeDuration | video 0.1–5, audio 0–10 seconds | video 1, audio 0 | With transition: 'crossfade'. |
gapDuration | 0–10 seconds | 0 | Audio — silence between tracks. |
normalizeAudio | boolean | true | Video, audio. |
reEncodeMode | auto · always · never | auto | Video. |
codec | h264 · h265 · vp9 · av1 | h264 | Video, when re-encoding. av1 is beta. |
crf / preset | 0–51 / ultrafast … veryslow | 23 / medium | Video, when re-encoding. |
targetResolution | 'WxH', e.g. '1920x1080' | inputs' size | Video, when re-encoding. |
targetSize | bytes, ≥ 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 option | Range | Applies to |
|---|---|---|
transition | as above | Video, audio — the join before this clip. |
crossfadeDuration | video 0.1–5, audio 0–10 seconds | Video, audio — with transition: 'crossfade'. |
gapDuration | 0–10 seconds | Audio only — dropped on a video merge. |
trimStart | ≥ 0 seconds | Video, audio — cut from the start of this clip, before any transition. |
trimEnd | ≥ 0 seconds | Video, 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.
- TypeScript
- PHP
- Python
- Rust
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' });
use Gisl\Sdk\Ergonomic\{ClipOptions, Merge, MergeOptions, RunOptions};
$intro = Merge::asset('./intro.mp4');
$body = Merge::asset('./body.mp4');
$result = $client
->merge([$intro, $body], new MergeOptions(mediaKind: 'video', transition: 'crossfade'))
->sequence([
$intro,
Merge::clip($body, new ClipOptions(transition: 'none', trimStart: 2.0, trimEnd: 1.5)),
Merge::clip($intro, new ClipOptions(transition: 'fade')),
])
->run(new RunOptions(maxWait: '20m'));
Not available: the Python SDK is not published and no release is scheduled — see Python.
Not an application SDK: Rust is internal contract types — see Rust.
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.