Video editing from PHP, without FFmpeg on your machine.
Trim, resize, compress, convert, caption and transcribe by calling one HTTP endpoint per operation. Parameters are validated before anything renders, so a bad call fails as a typed error rather than a broken encode.
What running FFmpeg from PHP usually costs
- Shared hosting commonly disables shell_exec and proc_open, which rules out every FFmpeg wrapper at once.
- Where it is allowed, the render runs inside a request and hits max_execution_time long before a real video is finished.
- Escaping a filter graph through the shell is its own category of bug, and it fails at render time rather than at review time.
Trim a clip and wait for the file
PHP
<?php
$KINOPIPE = 'https://kinopipe.com/api/v1';
$headers = [
'Authorization: Bearer ' . getenv('KINOPIPE_API_KEY'),
'Content-Type: application/json',
];
function kinopipe(string $url, array $headers, ?array $body = null): array {
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
]);
if ($body !== null) {
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($curl);
curl_close($curl);
return json_decode($response, true);
}
$started = kinopipe("$KINOPIPE/tools/trim-video", $headers, [
'inputs' => [['id' => 'main', 'url' => 'https://example.com/interview.mp4']],
'options' => ['start' => 12, 'end' => 48],
]);
do {
sleep(2);
$job = kinopipe("$KINOPIPE/jobs/{$started['jobId']}", $headers);
} while (!in_array($job['status'], ['succeeded', 'failed'], true));
if ($job['status'] === 'failed') {
throw new RuntimeException($job['errorMessage'] ?? 'the job did not finish');
}
echo $job['result']['output']['downloadUrl'];- Nothing here needs an extension beyond cURL, which shared hosting keeps enabled even when it forbids running processes.
- For a long render, store the job id and poll it from a cron task rather than holding the request open.
- Send an Idempotency-Key header when you retry, and the retry returns the original job instead of paying for a second render.
FAQ
My host disables shell_exec. Does this still work?
Yes. Nothing is executed on your server: the code makes HTTP requests, which is exactly what shared hosting is happy to allow.
How do I avoid max_execution_time?
Submit the job, save the id, and return. A cron task or a webhook picks up the finished file, so no request waits for an encode.
What about php-ffmpeg?
php-ffmpeg drives a local binary through the shell. It is the right tool on a server you control, and unavailable on most hosting where PHP actually runs.