Video Trimming Widget with Cloudinary

Ifeoma Imoh

The Cloudinary Media Editor is an interactive user interface that provides users on your website with common image and video editing actions. In this post, we will build a demo application showing how to configure the Cloudinary Media Editor widget to trim the length of videos. Using the application, a user can select and upload videos to Cloudinary. Upon successful upload, the publicID of the uploaded video will be used to configure the Media Editor widget.

Here is a link to the demo on CodeSandbox.

Project Setup

Create a new React app using the following command:

1npx create-react-app cloudinary_video_trimming

Let’s start our application on http://localhost:3000/ using the following command:

1npm start

Setting up Cloudinary

First, sign up for a free Cloudinary account if you don’t have one already. Displayed on your account’s Management Console (aka Dashboard) are important details: your cloud name, API key, etc.

We will send images to Cloudinary via unsigned POST requests, and to do this, we need our account cloud name and an unsigned upload preset. We need to create some environment variables to hold our Cloudinary details. Create a new file called .env at the root of the project and add the following to it:

1NEXT_PUBLIC_CLOUD_NAME = "INSERT YOUR CLOUD NAME HERE";
2NEXT_PUBLIC_UPLOAD_PRESET = "INSERT YOUR UNSIGNED UPLOAD PRESET KEY HERE";

This will be used as a default when the project is set up on another system. To update your local environment, create a copy of the .env file using this command:

1cp .env .env.local

By default, this local file is added to .gitignore. You can update .env.local with your cloud name and generated upload preset.

In the project's src directory, create a new folder named cloudinary. In the cloudinary folder, create a new file called cloudinaryConfig.js. Add the following to the cloudinaryConfig.js file:

1export const cloudName = process.env.REACT_APP_CLOUD_NAME;
2export const uploadPreset = process.env.REACT_APP_UPLOAD_PRESET;

This file will give access to the environment variables and prevent repeated process.env. calls throughout the project.

Building the Frontend

To configure the Media Editor, one of the properties we need to specify is the PublicID of the video to edit. After a user selects a video from their computer, we need to first upload that video to Cloudinary to get the PublicID.

Replace the content of your App.js file with the following:

1import "./App.css";
2import { cloudName, uploadPreset } from "./utils/cloudinaryConfig";
3
4function App() {
5 const handleUpload = async (e) => {
6 const clUrl = `https://api.cloudinary.com/v1_1/${cloudName}/video/upload`;
7 const file = e.target.files[0];
8 const formData = new FormData();
9 formData.append("file", file);
10 formData.append("upload_preset", uploadPreset);
11 const res = await fetch(clUrl, {
12 method: "POST",
13 body: formData,
14 });
15 const data = await res.json();
16 const publicId = data.public_id;
17 const duration = data.duration;
18 };
19
20 return (
21 <div className="App">
22 <input type="file" accept="video/*" onChange={handleUpload} />
23 </div>
24 );
25}
26
27export default App;

This component renders a file input that calls the handleUpload function when clicked. In the handleUpload function, we are making a POST request to Cloudinary using the previously defined cloud name and upload preset. Once the file has been uploaded, parse the response to JSON, then we extract the public ID and the duration (length of the video) of the uploaded video file from the data object, which will be used to configure the Media Editor widget.

Instantiate Media Editor Widget

Open the public/index.html file and add the following <script> tag in the head of the HTML file:

1<script
2 src="https://media-editor.cloudinary.com/all.js"
3 type="text/javascript"
4></script>

The functionality for the gallery widget is delivered via CDN and instantiated in the index.js file.

Next, update the src/App.js file to match the following:

1import "./App.css";
2 import { cloudName, uploadPreset } from "./cloudinary/cloudinaryConfig";
3
4 function App() {
5
6 function editVideo(publicId) {
7 /* eslint-disable */
8 const myEditor = cloudinary.mediaEditor();
9 myEditor.update({
10 cloudName: cloudName,
11 publicIds: [
12 {
13 publicId: publicId,
14 resourceType: "video",
15 },
16 ],
17 });
18 myEditor.show();
19 }
20
21 const handleUpload = async (e) => {
22 //...
23 };
24
25 return (
26 //...
27 );
28 }
29
30 export default App;

In the code above, we defined a function called editVideo that accepts publicId as props, which will be the public ID of the uploaded video. In the function, we initialize the Media Editor widget with the cloudinary.mediaEditor() method call.

Using the update method, we configure the widget with an object containing configuration parameters — our cloud name, the public ID of the video to edit, and we also include the resourceType property set to video. Finally, we call the show method to display the initialized widget.

We used the eslint-disable annotation to avoid eslint throwing an undefined variable exception.

Next, add these to the bottom of the handleUpload function like so:

1const handleUpload = async (e) => {
2 //...
3 editVideo(publicId);
4};

We’re calling the editVideo function and passing the public ID of the video uploaded. This open’s up the Media Editor widget with the video for editing.

Configuring the Media Widget

To set up the Media Editor for video editing, we need to pass the video editing parameters to a video object parameter within the cloudinary.mediaEditor() method call like so:

1function editVideo(publicId) {
2 /* eslint-disable */
3 const myEditor = cloudinary.mediaEditor();
4 myEditor.update({
5 cloudName: cloudName,
6 publicIds: [
7 {
8 publicId: publicId,
9 resourceType: "video",
10 },
11 ],
12 // add this
13 video: {
14 steps: ["trim"],
15 trim: {
16 startOffset: 0,
17 endOffset: duration,
18 maxDuration: 10,
19 minDuration: 4,
20 },
21 },
22 });
23 myEditor.show();
24}

In the code above, we passed the steps parameter to a video object parameter. It contains the trim step, which is the step we want to include in the video widget and the only step currently supported by the widget.

This step will enable a user to manually trim the video length using the trim handles. We define the initial location of the trim handles with the startOffset and endOffset properties. startOffset is set to 0 indicating that we want it to start from the beginning of the video, and endOffset is set to duration which is a state variable that will hold the length of the video.

We also define some optional properties: the maxDuration set to 10 and minDuration set to 4 for the trimmed video.

Now let’s define the state variable that will hold the length of the video. Update your App.js file with the following:

1//Import this to the top of the file
2 import { useState } from "react";
3
4 function App() {
5 const [duration, setDuration] = useState(5);
6
7 function editVideo(publicId) {
8 /* eslint-disable */
9 //...
10 }
11 const handleUpload = async (e) => {
12 //...
13 editVideo(publicId);
14 // add this
15 setDuration(duration);
16 };
17
18 return (
19 //...
20 );
21 }
22 export default App;

In the code above, we added a state variable to keep track of the length of the uploaded video, which we extracted from the response data object after the video was uploaded to Cloudinary.

Now, you can save the changes and open your browser to test the application.

You should be able to select a video, and after it has been uploaded, manually select the portion of the video you want to trim using the trim handles.

Export Trimmed Video

After selecting the section of the video we want to trim, what happens next? Well, it depends on what you want for your application. You can register a Media Editor event to introduce custom behavior in your application or to track analytics once your application is deployed.

For our application, we want to be able to download the video after it has been trimmed. Notice the Media Editor has an Export button. At the moment, if you click that button, it closes the Media Editor widget instead of downloading the video.

This is because we need to register to the export event, which will be called when the Export button is clicked, giving us access to the final delivery URLs. It returns an array of URLs containing the url, secureUrl, and downloadUrl.

In your App.js file, add the following to the update method below myEditor.show():

1myEditor.on("export", function (data) {
2 console.log(data);
3 window.open(data.assets[0].downloadUrl, "_parent");
4});

We’re using the on method to register to an export event and then the window.open method to open the downloadUrl in a new window so that it can be downloaded.

Save the changes and open your browser to test the application.

Find the complete project here on GitHub.

Conclusion

In this article, we looked at how to configure the Cloudinary Media Editor widget in a simple React application to trim the length of videos. Video support in the Media Editor is currently in Beta, and you can click here to send information to the support team. Click here for details on how to use the Media Editor to perform image editing actions.

Here are some resources you may find helpful:

Ifeoma Imoh

Software Developer

Ifeoma is a software developer and technical content creator in love with all things JavaScript.