DocsGuides
Join files
Put videos or sounds one after another into one file.
POST /v1/concat plays its inputs one after another, picture and sound, in one file.
The request#
curl "$VIDEXO_API_URL/v1/concat" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
"https://example.com/intro.mp4",
"https://example.com/talk.mp4"
],
"filename": "talk-with-intro.mp4",
"resize": {
"width": 1920,
"height": 1080,
"fit": "pad"
}
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/concat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"inputs": [
"https://example.com/intro.mp4",
"https://example.com/talk.mp4"
],
"filename": "talk-with-intro.mp4",
"resize": {
"width": 1920,
"height": 1080,
"fit": "pad"
}
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
ffmpeg \
-i https://example.com/intro.mp4 \
-i https://example.com/talk.mp4 \
-filter_complex "[0:v]setpts=PTS-STARTPTS,scale=1920:1080:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c0v];[0:a]asetpts=PTS-STARTPTS[o0c0a];[1:v]setpts=PTS-STARTPTS,scale=1920:1080:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c1v];[1:a]asetpts=PTS-STARTPTS[o0c1a];[o0c0v][o0c0a][o0c1v][o0c1a]concat=n=2:v=1:a=1[o0cv][o0ca]" \
-map "[o0cv]" \
-c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \
-map "[o0ca]" \
-c:a aac -b:a 128k -movflags +faststart \
talk-with-intro.mp4Copy failed. The text is selected; copy it with your keyboard.
talk-with-intro.mp4: the intro, then the talk, as one 1920 by 1080 video at 30 frames a second with one audio track; a part of another shape gets bars.
Parts differ in size, so a video needs a resize box; every part is fitted into it at fps (30 by
default), with bars where its shape differs. Each part must contain an audio track unless the
output sets "audio": false.
Parts of inputs#
A part can be a time range of its input. The same input may appear more than once.
curl "$VIDEXO_API_URL/v1/concat" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
"https://example.com/intro.mp4",
{
"source": "https://example.com/talk.mp4",
"start": 10,
"end": 70
},
"https://example.com/outro.mp4"
],
"resize": {
"width": 1280,
"height": 720
}
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/concat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"inputs": [
"https://example.com/intro.mp4",
{
"source": "https://example.com/talk.mp4",
"start": 10,
"end": 70
},
"https://example.com/outro.mp4"
],
"resize": {
"width": 1280,
"height": 720
}
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
ffmpeg \
-i https://example.com/intro.mp4 \
-ss 10 -t 60 -i https://example.com/talk.mp4 \
-i https://example.com/outro.mp4 \
-filter_complex "[0:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c0v];[0:a]asetpts=PTS-STARTPTS[o0c0a];[1:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c1v];[1:a]asetpts=PTS-STARTPTS[o0c1a];[2:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c2v];[2:a]asetpts=PTS-STARTPTS[o0c2a];[o0c0v][o0c0a][o0c1v][o0c1a][o0c2v][o0c2a]concat=n=3:v=1:a=1[o0cv][o0ca]" \
-map "[o0cv]" \
-c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \
-map "[o0ca]" \
-c:a aac -b:a 128k -movflags +faststart \
video.mp4Copy failed. The text is selected; copy it with your keyboard.
Sound only#
curl "$VIDEXO_API_URL/v1/concat" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "audio",
"inputs": [
"https://example.com/part-1.mp3",
"https://example.com/part-2.mp3"
],
"format": "mp3"
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/concat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "audio",
"inputs": [
"https://example.com/part-1.mp3",
"https://example.com/part-2.mp3"
],
"format": "mp3"
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
ffmpeg \
-i https://example.com/part-1.mp3 \
-i https://example.com/part-2.mp3 \
-filter_complex "[0:a]asetpts=PTS-STARTPTS[o0c0a];[1:a]asetpts=PTS-STARTPTS[o0c1a];[o0c0a][o0c1a]concat=n=2:v=0:a=1[o0ca]" \
-vn \
-map "[o0ca]" \
-c:a libmp3lame -b:a 128k \
audio.mp3Copy failed. The text is selected; copy it with your keyboard.
Transitions#
Give a part a transition to cross into it from the part before, picture and sound. duration is 0.1
to 5 seconds, 1 by default. The parts overlap by it, so the output is that much shorter.
curl "$VIDEXO_API_URL/v1/concat" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"source": "https://example.com/intro.mp4",
"end": 5
},
{
"source": "https://example.com/talk.mp4",
"start": 10,
"end": 70,
"transition": {
"type": "dissolve",
"duration": 0.5
}
}
],
"resize": {
"width": 1280,
"height": 720
}
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/concat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"inputs": [
{
"source": "https://example.com/intro.mp4",
"end": 5
},
{
"source": "https://example.com/talk.mp4",
"start": 10,
"end": 70,
"transition": {
"type": "dissolve",
"duration": 0.5
}
}
],
"resize": {
"width": 1280,
"height": 720
}
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
ffmpeg \
-t 5 -i https://example.com/intro.mp4 \
-ss 10 -t 60 -i https://example.com/talk.mp4 \
-filter_complex "[0:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30,format=yuv420p,tpad=stop=-1:stop_mode=clone,trim=end_frame=150[o0c0v];[0:a]asetpts=PTS-STARTPTS,apad=whole_dur=5,atrim=duration=5[o0c0a];[1:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30,format=yuv420p,tpad=stop=-1:stop_mode=clone,trim=end_frame=1800[o0c1v];[1:a]asetpts=PTS-STARTPTS,apad=whole_dur=60,atrim=duration=60[o0c1a];[o0c0v][o0c1v]xfade=transition=dissolve:duration=0.5:offset=4.5[o0cv];[o0c0a][o0c1a]acrossfade=d=0.5:c1=tri:c2=tri[o0ca]" \
-map "[o0cv]" \
-c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \
-map "[o0ca]" \
-c:a aac -b:a 128k -movflags +faststart \
video.mp4Copy failed. The text is selected; copy it with your keyboard.
type is one of fade, fade-black, fade-white, dissolve, wipe-left, wipe-right, wipe-up,
wipe-down, slide-left, slide-right, slide-up, slide-down, circle-open, circle-close,
radial, zoom-in or pixelize.
- Every part before a transition needs an
end, so its length is known. Only the last part may run to the end of its file. - A part must be longer than the transitions at both of its ends.
- A part that runs out before its
endholds its last frame and goes silent until then. - Sound alone (
"type": "audio") cannot take a transition.
Title cards#
On POST /v1/jobs, a part may be a title card: a solid colour for duration seconds, from 0.1 to 300,
with up to 4 text layers timed from the start of the card. It is generated, so it has no file, and it
takes the size and frame rate of the video. It is silent next to clips that keep their sound.
curl "$VIDEXO_API_URL/v1/jobs" \
-H "Authorization: Bearer $VIDEXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {
"talk": "https://example.com/talk.mp4"
},
"outputs": [
{
"type": "video",
"filename": "with-title.mp4",
"resize": {
"width": 1280,
"height": 720
},
"concat": [
{
"color": "#101828",
"duration": 3,
"text": [
{
"text": "Launch day",
"font": "inter-bold",
"size": 72
}
]
},
{
"input": "talk",
"start": 10,
"end": 70
}
]
}
]
}'Copy failed. The text is selected; copy it with your keyboard.
const response = await fetch(`${process.env.VIDEXO_API_URL}/v1/jobs`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VIDEXO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"inputs": {
"talk": "https://example.com/talk.mp4"
},
"outputs": [
{
"type": "video",
"filename": "with-title.mp4",
"resize": {
"width": 1280,
"height": 720
},
"concat": [
{
"color": "#101828",
"duration": 3,
"text": [
{
"text": "Launch day",
"font": "inter-bold",
"size": 72
}
]
},
{
"input": "talk",
"start": 10,
"end": 70
}
]
}
]
}),
});
const job = await response.json();Copy failed. The text is selected; copy it with your keyboard.
ffmpeg \
-f lavfi -i color=c=0x101828:s=1280x720:r=30:d=3 \
-f lavfi -i anullsrc=r=48000:cl=stereo:d=3 \
-ss 10 -t 60 -i https://example.com/talk.mp4 \
-filter_complex "[0:v]trim=end_frame=90,drawtext=expansion=none:fontfile=/opt/videxo/fonts/Inter-Bold.ttf:text=Launch day:fontsize=72:fontcolor=0xFFFFFF:x=(w-tw)/2:y=(h-90)/2:y_align=font[o0c0v];[2:v]setpts=PTS-STARTPTS,scale=1280:720:force_original_aspect_ratio=decrease:force_divisible_by=2,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0x000000,setsar=1,fps=30[o0c1v];[2:a]asetpts=PTS-STARTPTS[o0c1a];[o0c0v][1:a][o0c1v][o0c1a]concat=n=2:v=1:a=1[o0cv][o0ca]" \
-map "[o0cv]" \
-c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \
-map "[o0ca]" \
-c:a aac -b:a 128k -movflags +faststart \
with-title.mp4Copy failed. The text is selected; copy it with your keyboard.
with-title.mp4: 3 seconds of title, silent, then a minute of the talk with its sound, at 1280 by 720.
color is black, white or #RRGGBB. A card takes a transition like any part. A job must read at
least one file as media, so a video of cards alone needs music from audio.input, an overlay or another
output that reads a file. A subtitles file or a metadata output does not count.