Create a video trim command from a slider concept

A concept to create a video trim FFmpeg command by using a slider to view the video at a start and end timeframe.

The easy slider integration is with the noUiSlider framework. FFmpeg on the backend to create the thumbnail files.

View the full index.php file here. The source video link.

Trimming video

To do a video trim or snip in FFmpeg you need the start time and then the duration for the end cut.

ffmpeg -ss 00:00:10 -i video.mp4 -t 00:00:25 -c copy trimmed.mp4

The above command will create a 15-second clip called trimmed.mp4 this clip is from the 10 second timeframe to the 25 second timeframe of video.mp4

The code is very similar to the thumbnail creator concept. However there are now 2 thumbnails and the slider time difference is calculated.

The PHP

Defines the video file and the 2 thumbnail image file names. Next is a check for if new thumbnails need to be created (slider was changed, POST request sent) or if none exists (first time running) create from the 1 second fragment.

<?php
$file = "jellyfish.mkv";
$thumb_start = "thumbnail1.jpg";
$thumb_finish = "thumbnail2.jpg";

if (isset($_POST['time_string'])) {
    if ($_POST['type'] === 'start') {
        exec("ffmpeg -y -ss {$_POST['time_string']} -i $file -vframes 1 -q:v 1 $thumb_start");
    } else {
        exec("ffmpeg -y -ss {$_POST['time_string']} -i $file -vframes 1 -q:v 1 $thumb_finish");
    }
} elseif (!file_exists("thumbnail1.jpg") || !file_exists("thumbnail2.jpg")) {
    exec("ffmpeg -y -ss 00:00:01 -i $file -vframes 1 -q:v 1 $thumb_start");
    exec("ffmpeg -y -ss 00:00:01 -i $file -vframes 1 -q:v 1 $thumb_finish");
}

A check is done for the length of the video in seconds. This value is used as the max value for the slider.

function getSeconds(string $file): float
{
    return exec("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $file");
}

$seconds = getSeconds($file);

The Javascript

This requires jQuery and the noUiSlider library. Note PHP is used to echo the seconds into the max value.

let slider = document.getElementById("slider");

noUiSlider.create(slider, {
    start: [0, <?=$seconds?>],
    connect: true,
    step: 1,
    range: {
        min: 0,
        max: <?=$seconds?>
    }
});

let start_element = document.getElementsByClassName("start");
let finish_element = document.getElementsByClassName("finish");

slider.noUiSlider.on("change", function (values, handle) {
    let old_start = start_element[0].innerHTML;
    let old_finish = finish_element[0].innerHTML;
    let slider_values = slider.noUiSlider.get();
    let start_string = new Date(slider_values[0] * 1000).toISOString().substr(11, 8);
    let finish_string = new Date(slider_values[1] * 1000).toISOString().substr(11, 8);
    let duration = new Date((slider_values[1] - slider_values[0]) * 1000).toISOString().substr(11, 8);
    start_element[0].innerHTML = start_string;
    start_element[1].innerHTML = start_string;
    finish_element[0].innerHTML = finish_string;
    finish_element[1].innerHTML = duration;
    if (old_start !== start_string) {
        createThumb(start_string, 'start');
    }
    if (old_finish !== finish_string) {
        createThumb(finish_string, 'finish');
    }
});

The Ajax POST request to create the thumbnail

function createThumb(time, type) {
    $.ajax({
        type: "POST",
        url: "index.php",
        data: {"time_string": time, "type": type},
        success: function (result) {
            let d = new Date();
            if (type === 'start') {
                $("#thumbnail-start").attr("src", "thumbnail1.jpg?" + d.getTime());
            } else {
                $("#thumbnail-finish").attr("src", "thumbnail2.jpg?" + d.getTime());
            }
        }
    });
}

The HTML

A Bootstrap card with the noui slider.

<div class="container">
    <div class="row">
        <div class="col-12">
            <div class="card shadow">
                <div class="card-header">
                    <h2 class="text-center">Create video trim command</h2>
                </div>
                <div class="card-body px-4">
                    <div class="row">
                        <div class="col-6">
                            <img id="thumbnail-start" class="img-fluid" src="<?= $thumb_start ?>" width="100%"
                                 alt="<?= $file ?> video thumbnail">
                        </div>
                        <div class="col-6">
                            <img id="thumbnail-finish" class="img-fluid" src="<?= $thumb_finish ?>" width="100%"
                                 alt="<?= $file ?> video thumbnail">
                        </div>
                    </div>
                    <div class="row text-center mt-4">
                        <div id="slider"></div>
                    </div>
                    <div class="row text-center mt-3">
                        <div class="col-6">
                            <p>Start: <code><span class="start">00:00:01</span></code></p>
                        </div>
                        <div class="col-6">
                            <p>Finish: <code><span class="finish"><?= gmdate("H:i:s", $seconds) ?></span></code></p>
                        </div>
                    </div>
                    <div class="row text-center mt-3">
                        <div class="col-12">
                            <code>ffmpeg -ss <span class="start">00:00:00</span> -i INPUT.mp4 -t <span class="finish">00:00:00</span>
                                -c copy OUTPUT.mp4</code>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>